From b44ce1af2eed4793c5ddb38f1a545deb7ac9aacb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jeremy=20Lain=C3=A9?= Date: Wed, 1 Apr 2009 18:48:50 +0200 Subject: linux-2.6.29: add patches for boc01 --- .../boc01/001-090114-sqn11x0-usb-hack.patch | 17 + .../linux/linux-2.6.29/boc01/004-081205-usb.patch | 16 + .../linux-2.6.29/boc01/005-090226-isl12024.patch | 693 +++++++ .../linux/linux-2.6.29/boc01/007-081217-lm73.patch | 269 +++ .../linux/linux-2.6.29/boc01/008-081208-spi.patch | 147 ++ .../linux/linux-2.6.29/boc01/011-090115-gpio.patch | 440 +++++ .../linux-2.6.29/boc01/012-090219-capsense.patch | 883 +++++++++ .../linux/linux-2.6.29/boc01/013-090306-lcd.patch | 1090 +++++++++++ recipes/linux/linux-2.6.29/boc01/boc01.dts | 340 ++++ recipes/linux/linux-2.6.29/boc01/defconfig | 2041 ++++++++++++++++++++ recipes/linux/linux_2.6.29.bb | 13 + 11 files changed, 5949 insertions(+) create mode 100644 recipes/linux/linux-2.6.29/boc01/001-090114-sqn11x0-usb-hack.patch create mode 100644 recipes/linux/linux-2.6.29/boc01/004-081205-usb.patch create mode 100644 recipes/linux/linux-2.6.29/boc01/005-090226-isl12024.patch create mode 100644 recipes/linux/linux-2.6.29/boc01/007-081217-lm73.patch create mode 100644 recipes/linux/linux-2.6.29/boc01/008-081208-spi.patch create mode 100644 recipes/linux/linux-2.6.29/boc01/011-090115-gpio.patch create mode 100644 recipes/linux/linux-2.6.29/boc01/012-090219-capsense.patch create mode 100644 recipes/linux/linux-2.6.29/boc01/013-090306-lcd.patch create mode 100644 recipes/linux/linux-2.6.29/boc01/boc01.dts create mode 100644 recipes/linux/linux-2.6.29/boc01/defconfig diff --git a/recipes/linux/linux-2.6.29/boc01/001-090114-sqn11x0-usb-hack.patch b/recipes/linux/linux-2.6.29/boc01/001-090114-sqn11x0-usb-hack.patch new file mode 100644 index 0000000000..4bd44e25db --- /dev/null +++ b/recipes/linux/linux-2.6.29/boc01/001-090114-sqn11x0-usb-hack.patch @@ -0,0 +1,17 @@ +diff -ur linux-2.6.27.orig/drivers/usb/host/ehci-q.c linux-2.6.27/drivers/usb/host/ehci-q.c +--- linux-2.6.27.orig/drivers/usb/host/ehci-q.c 2008-10-10 00:13:53.000000000 +0200 ++++ linux-2.6.27/drivers/usb/host/ehci-q.c 2009-01-06 18:31:38.000000000 +0100 +@@ -799,7 +799,12 @@ + * to help them do so. So now people expect to use + * such nonconformant devices with Linux too; sigh. + */ +- info1 |= max_packet(maxp) << 16; ++ struct usb_device_descriptor *desc = &urb->dev->descriptor; ++ if (desc->idVendor == cpu_to_le16(0x148e) && desc->idProduct == cpu_to_le16(0x0900)) { ++ info1 |= 256 << 16; ++ } else { ++ info1 |= max_packet(maxp) << 16; ++ } + info2 |= (EHCI_TUNE_MULT_HS << 30); + } else { /* PIPE_INTERRUPT */ + info1 |= max_packet (maxp) << 16; diff --git a/recipes/linux/linux-2.6.29/boc01/004-081205-usb.patch b/recipes/linux/linux-2.6.29/boc01/004-081205-usb.patch new file mode 100644 index 0000000000..dc7eba64d0 --- /dev/null +++ b/recipes/linux/linux-2.6.29/boc01/004-081205-usb.patch @@ -0,0 +1,16 @@ +--- linux-2.6.27.orig/arch/powerpc/platforms/83xx/usb.c 2008-12-05 14:50:43.000000000 +0100 ++++ linux-2.6.27.modif/arch/powerpc/platforms/83xx/usb.c 2008-12-05 14:51:17.000000000 +0100 +@@ -172,10 +172,10 @@ + !strcmp(prop, "utmi"))) { + u32 refsel; + +- if (of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) ++// if (of_device_is_compatible(immr_node, "fsl,mpc8315-immr")) + refsel = CONTROL_REFSEL_24MHZ; +- else +- refsel = CONTROL_REFSEL_48MHZ; ++// else ++// refsel = CONTROL_REFSEL_48MHZ; + /* Set UTMI_PHY_EN and REFSEL */ + out_be32(usb_regs + FSL_USB2_CONTROL_OFFS, + CONTROL_UTMI_PHY_EN | refsel); diff --git a/recipes/linux/linux-2.6.29/boc01/005-090226-isl12024.patch b/recipes/linux/linux-2.6.29/boc01/005-090226-isl12024.patch new file mode 100644 index 0000000000..ce71c3af80 --- /dev/null +++ b/recipes/linux/linux-2.6.29/boc01/005-090226-isl12024.patch @@ -0,0 +1,693 @@ +Index: linux-2.6.29/drivers/misc/eeprom/at24.c +=================================================================== +--- linux-2.6.29.orig/drivers/misc/eeprom/at24.c 2009-03-24 00:12:14.000000000 +0100 ++++ linux-2.6.29/drivers/misc/eeprom/at24.c 2009-04-01 17:37:45.000000000 +0200 +@@ -114,6 +114,8 @@ + { "spd", AT24_DEVICE_MAGIC(2048 / 8, + AT24_FLAG_READONLY | AT24_FLAG_IRUGO) }, + { "24c04", AT24_DEVICE_MAGIC(4096 / 8, 0) }, ++ /* Intersil RTC/Unique-ID isl12024 eeprom handled here */ ++ { "isl12024",AT24_DEVICE_MAGIC(4096 / 8, AT24_FLAG_ADDR16) }, + /* 24rf08 quirk is handled at i2c-core */ + { "24c08", AT24_DEVICE_MAGIC(8192 / 8, 0) }, + { "24c16", AT24_DEVICE_MAGIC(16384 / 8, 0) }, +Index: linux-2.6.29/drivers/rtc/Kconfig +=================================================================== +--- linux-2.6.29.orig/drivers/rtc/Kconfig 2009-03-24 00:12:14.000000000 +0100 ++++ linux-2.6.29/drivers/rtc/Kconfig 2009-04-01 17:37:45.000000000 +0200 +@@ -128,6 +128,12 @@ + + if I2C + ++config RTC_DRV_ISL12024 ++ tristate "Intersil 12024 RTC/ UniqueID" ++ help ++ If you say yes .... ++ This driver can also be built as a module. ++ + config RTC_DRV_DS1307 + tristate "Dallas/Maxim DS1307/37/38/39/40, ST M41T00" + help +Index: linux-2.6.29/drivers/rtc/Makefile +=================================================================== +--- linux-2.6.29.orig/drivers/rtc/Makefile 2009-03-24 00:12:14.000000000 +0100 ++++ linux-2.6.29/drivers/rtc/Makefile 2009-04-01 17:37:45.000000000 +0200 +@@ -39,6 +39,7 @@ + obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o + obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o + obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o ++obj-$(CONFIG_RTC_DRV_ISL12024) += rtc-isl12024.o + obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o + obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o + obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o +Index: linux-2.6.29/drivers/rtc/isl12024.h +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.29/drivers/rtc/isl12024.h 2009-04-01 17:37:45.000000000 +0200 +@@ -0,0 +1,100 @@ ++/* ++ * Intersil ISL12024 chip registers definitions ++ * ++ * ++ * Copyright (C) 2008, CenoSYS (www.cenosys.com). ++ * Guillaume Ligneul ++ * Guillaume.ligneul@gmail.com ++ * ++ * This software program is licensed subject to the GNU General Public License ++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html ++ */ ++ ++#ifndef ISL12024_H_ ++#define ISL12024_H_ ++ ++#define ISL12024_REG_SR 0x3F /* status register */ ++#define ISL12024_REG_Y2K 0x37 ++#define ISL12024_REG_DW 0x36 ++#define ISL12024_REG_YR 0x35 ++#define ISL12024_REG_MO 0x34 ++#define ISL12024_REG_DT 0x33 ++#define ISL12024_REG_HR 0x32 ++#define ISL12024_REG_MN 0x31 ++#define ISL12024_REG_SC 0x30 ++#define ISL12024_REG_DTR 0x13 ++#define ISL12024_REG_ATR 0x12 ++#define ISL12024_REG_INT 0x11 ++#define ISL12024_REG_0 0x10 ++#define ISL12024_REG_Y2K1 0x0F ++#define ISL12024_REG_DWA1 0x0E ++#define ISL12024_REG_YRA1 0x0D ++#define ISL12024_REG_MOA1 0x0C ++#define ISL12024_REG_DTA1 0x0B ++#define ISL12024_REG_HRA1 0x0A ++#define ISL12024_REG_MNA1 0x09 ++#define ISL12024_REG_SCA1 0x08 ++#define ISL12024_REG_Y2K0 0x07 ++#define ISL12024_REG_DWA0 0x06 ++#define ISL12024_REG_YRA0 0x05 ++#define ISL12024_REG_MOA0 0x04 ++#define ISL12024_REG_DTA0 0x03 ++#define ISL12024_REG_HRA0 0x02 ++#define ISL12024_REG_MNA0 0x01 ++#define ISL12024_REG_SCA0 0x00 ++ ++#define ISL12024_CCR_BASE 0x30 /* Base address of CCR */ ++#define ISL12024_ALM0_BASE 0x00 /* Base address of ALARM0 */ ++ ++#define ISL12024_SR_RTCF 0x01 /* Clock failure */ ++#define ISL12024_SR_WEL 0x02 /* Write Enable Latch */ ++#define ISL12024_SR_RWEL 0x04 /* Register Write Enable */ ++#define ISL12024_SR_AL0 0x20 /* Alarm 0 match */ ++ ++#define ISL12024_DTR_DTR0 0x01 ++#define ISL12024_DTR_DTR1 0x02 ++#define ISL12024_DTR_DTR2 0x04 ++ ++#define ISL12024_HR_MIL 0x80 /* Set in ccr.hour for 24 hr mode */ ++ ++#define ISL12024_INT_AL0E 0x20 /* Alarm 0 enable */ ++ ++/* I2C ADDRESS */ ++#define ISL12024_I2C_ADDR 0xDE ++#define ISL12024_I2C_EEPROM_ADDR 0x57 ++ ++/* device id section */ ++#define ISL12024_REG_ID 0x20 ++ ++/* Register map */ ++/* rtc section */ ++#define ISL12024_REG_HR_MIL (1<<7) /* 24h/12h mode */ ++#define ISL12024_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */ ++//#define ISL12024_REG_DT 0x33 /* Date */ ++//#define ISL12024_REG_MO 0x34 /* Month */ ++//#define ISL12024_REG_YR 0x35 /* Year */ ++//#define ISL12024_REG_DW 0x36 ++//#define ISL12024_REG_Y2K 0x37 ++#define ISL12024_RTC_SECTION_LEN 8 ++ ++/* control/status section */ ++//#define ISL12024_REG_SR 0x3F ++//#define ISL12024_REG_SR_BAT (1<<7) /* battery */ ++//#define ISL12024_REG_SR_AL1 (1<<6) /* alarm 0 */ ++//#define ISL12024_REG_SR_AL0 (1<<5) /* alarm 1 */ ++//#define ISL12024_REG_SR_OSCF (1<<4) /* oscillator fail */ ++//#define ISL12024_REG_SR_RWEL (1<<2) /* register write enable latch */ ++//#define ISL12024_REG_SR_WEL (1<<1) /* write enable latch */ ++//#define ISL12024_REG_SR_RTCF (1<<0) /* rtc fail */ ++//#define ISL12024_REG_INT 0x11 ++ ++#define CCR_SEC 0 ++#define CCR_MIN 1 ++#define CCR_HOUR 2 ++#define CCR_MDAY 3 ++#define CCR_MONTH 4 ++#define CCR_YEAR 5 ++#define CCR_WDAY 6 ++#define CCR_Y2K 7 ++ ++#endif /*ISL12024_H_*/ +Index: linux-2.6.29/drivers/rtc/rtc-isl12024.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.29/drivers/rtc/rtc-isl12024.c 2009-04-01 17:37:45.000000000 +0200 +@@ -0,0 +1,541 @@ ++/* ++ * Intersil ISL12024 class driver ++ * ++ * ++ * Copyright (C) 2007, CenoSYS (www.cenosys.com). ++ * ++ * Guillaume Ligneul ++ * Sylvain Giroudon ++ * ++ * This software program is licensed subject to the GNU General Public License ++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#include "isl12024.h" ++ ++ ++#define DBG 1 ++#undef DBG ++ ++#define DRV_NAME "isl12024" ++#define DRV_VERSION "0.2" ++ ++/* i2c configuration */ ++static const unsigned short normal_i2c[] = { ++ ISL12024_I2C_ADDR >>1, I2C_CLIENT_END ++}; ++I2C_CLIENT_INSMOD; ++ ++static int isl12024_get_status(struct i2c_client *client, unsigned char *sr); ++static int isl12024_fix_osc(struct i2c_client *client); ++ ++static int isl12024_attach_adapter(struct i2c_adapter *adapter); ++static int isl12024_detach_client(struct i2c_client *client); ++ ++ ++/* Bufer to store unique identifier in */ ++static u8 buf_id[ISL12024_RTC_SECTION_LEN] = { 0 }; ++ ++ ++// To debug (may be added in include/linux/i2c-id.h) ++#define I2C_DRIVERID_ISL12024 97 ++ ++ ++static struct i2c_driver isl12024_driver = { ++ .driver = { ++ .name = DRV_NAME, ++ }, ++ .id = I2C_DRIVERID_ISL12024, ++ .attach_adapter = &isl12024_attach_adapter, ++ .detach_client = &isl12024_detach_client, ++}; ++ ++ ++int ++isl12024_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[], ++ unsigned len) ++{ ++ int ret; ++ u8 dt_addr[2]; ++ ++ struct i2c_msg msgs[2] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = 2, ++ .buf = dt_addr, ++ }, ++ { ++ .addr = client->addr, ++ .flags = I2C_M_RD, ++ .len = len , ++ .buf = buf , ++ }, ++ }; ++ ++ dt_addr[0] = 0; ++ dt_addr[1] = reg; ++ ++ ret = i2c_transfer(client->adapter, msgs, 2); ++ if ( ret < 0) { ++ dev_err(&client->dev, "read error\n"); ++ return -EIO; ++ } ++ return ret; ++} ++ ++EXPORT_SYMBOL(isl12024_i2c_read_regs); ++ ++ ++int ++isl12024_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[], ++ unsigned len) ++{ ++ int ret; ++ u8 i2c_buf[10]; ++ ++ struct i2c_msg msgs[1] = { ++ { ++ .addr = client->addr, ++ .flags = 0, ++ .len = len+2, ++ .buf = i2c_buf, ++ }, ++ }; ++ ++ i2c_buf[0] = 0; ++ i2c_buf[1] = reg; ++ ++ ++ memcpy(&i2c_buf[2], &buf[0], len ); ++ ++ ret = i2c_transfer(client->adapter, msgs, 1); ++ if ( ret < 0 ) ++ printk(KERN_ERR DRV_NAME ": i2c_transfer failed (%d)\n", ret); ++ ++ return ret; ++} ++ ++EXPORT_SYMBOL(isl12024_i2c_set_regs); ++ ++ ++static int isl12024_i2c_validate_client(struct i2c_client *client) ++{ ++ u8 regs[ISL12024_RTC_SECTION_LEN] = { 0, }; ++ u8 zero_mask[ISL12024_RTC_SECTION_LEN] = { ++ 0x80, 0x80, 0x40, 0xc0, 0xe0, 0x00, 0xf8, 0xc6 ++ }; ++ ++ int i; ++ int ret; ++ ++ ret = isl12024_i2c_read_regs(client, ISL12024_REG_SC, regs, ISL12024_RTC_SECTION_LEN); ++ ++ if (ret < 0) ++ return ret; ++ ++ for (i = 0; i < ISL12024_RTC_SECTION_LEN; ++i) { ++ if (regs[i] & zero_mask[i]) /* check if bits are cleared */ ++ return -ENODEV; ++ ++ } ++ ++ return 0; ++} ++ ++ ++static int isl12024_read_time(struct i2c_client *client, ++ struct rtc_time *tm) ++{ ++ unsigned char sr; ++ int err; ++ u8 regs[ISL12024_RTC_SECTION_LEN] = { 0, }; ++ ++ //printk(KERN_INFO DRV_NAME "%s\n ",__FUNCTION__ ); ++ ++ if (isl12024_get_status(client, &sr) < 0) { ++ dev_err(&client->dev, "%s: reading SR failed\n", __func__); ++ return -EIO; ++ } ++ ++ err = isl12024_i2c_read_regs(client, ISL12024_REG_SC, regs, ISL12024_RTC_SECTION_LEN); ++ ++#ifdef DBG ++ int i; ++ for(i=0; idev, "%s: reading RTC section failed\n", ++ __func__); ++ return sr; ++ } ++ ++ tm->tm_sec = bcd2bin(regs[0]); ++ tm->tm_min = bcd2bin(regs[1]); ++ ++ { /* HR field has a more complex interpretation */ ++ const u8 _hr = regs[2]; ++ if (_hr & ISL12024_REG_HR_MIL) /* 24h format */ ++ tm->tm_hour = bcd2bin(_hr & 0x3f); ++ else { // 12h format ++ tm->tm_hour = bcd2bin(_hr & 0x1f); ++ if (_hr & ISL12024_REG_HR_PM) /* PM flag set */ ++ tm->tm_hour += 12; ++ } ++ } ++ ++ tm->tm_mday = bcd2bin(regs[3]); ++ tm->tm_mon = bcd2bin(regs[4]); ++ tm->tm_year = bcd2bin(regs[5]) + 100; ++ tm->tm_wday = bcd2bin(regs[6]); ++ ++ return rtc_valid_tm(tm); ++} ++ ++ ++static int isl12024_get_status(struct i2c_client *client, unsigned char *sr) ++{ ++ static unsigned char sr_addr[2] = { 0, ISL12024_REG_SR }; ++ ++ struct i2c_msg msgs[] = { ++ { client->addr, 0, 2, sr_addr }, /* setup read ptr */ ++ { client->addr, I2C_M_RD, 1, sr }, /* read status */ ++ }; ++ ++ /* read status register */ ++ if (i2c_transfer(client->adapter, &msgs[0], 2) != 2) { ++ dev_err(&client->dev, "%s: read error\n", __func__); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++ ++static int isl12024_set_datetime(struct i2c_client *client, struct rtc_time *tm, ++ int datetoo, u8 reg_base, unsigned char alm_enable) ++{ ++ int i, xfer, nbytes; ++ unsigned char buf[8]; ++ unsigned char rdata[10] = { 0, reg_base }; ++ ++ static const unsigned char wel[3] = { 0, ISL12024_REG_SR, ++ ISL12024_SR_WEL }; ++ ++ static const unsigned char rwel[3] = { 0, ISL12024_REG_SR, ++ ISL12024_SR_WEL | ISL12024_SR_RWEL }; ++ ++ static const unsigned char diswe[3] = { 0, ISL12024_REG_SR, 0 }; ++ ++ dev_dbg(&client->dev, ++ "%s: secs=%d, mins=%d, hours=%d\n", ++ __func__, ++ tm->tm_sec, tm->tm_min, tm->tm_hour); ++ ++ buf[CCR_SEC] = bin2bcd(tm->tm_sec); ++ buf[CCR_MIN] = bin2bcd(tm->tm_min); ++ ++ /* set hour and 24hr bit */ ++ buf[CCR_HOUR] = bin2bcd(tm->tm_hour) | ISL12024_HR_MIL; ++ ++ /* should we also set the date? */ ++ if (datetoo) { ++ dev_dbg(&client->dev, ++ "%s: mday=%d, mon=%d, year=%d, wday=%d\n", ++ __func__, ++ tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday); ++ ++ buf[CCR_MDAY] = bin2bcd(tm->tm_mday); ++ ++ /* month, 1 - 12 */ ++ buf[CCR_MONTH] = bin2bcd(tm->tm_mon); ++ ++ /* year, since the rtc epoch*/ ++ buf[CCR_YEAR] = bin2bcd(tm->tm_year % 100); ++ buf[CCR_WDAY] = tm->tm_wday & 0x07; ++ buf[CCR_Y2K] = bin2bcd(tm->tm_year / 100); ++ } ++ ++ /* If writing alarm registers, set compare bits on registers 0-4 */ ++ if (reg_base < ISL12024_CCR_BASE) ++ for (i = 0; i <= 4; i++) ++ buf[i] |= 0x80; ++ ++ /* this sequence is required to unlock the chip */ ++ if ((xfer = i2c_master_send(client, wel, 3)) != 3) { ++ dev_err(&client->dev, "%s: wel - %d\n", __func__, xfer); ++ return -EIO; ++ } ++ ++ if ((xfer = i2c_master_send(client, rwel, 3)) != 3) { ++ dev_err(&client->dev, "%s: rwel - %d\n", __func__, xfer); ++ return -EIO; ++ } ++ ++ ++ /* write register's data */ ++ if (datetoo) ++ nbytes = 8; ++ else ++ nbytes = 3; ++ for (i = 0; i < nbytes; i++) ++ rdata[2+i] = buf[i]; ++ ++ xfer = i2c_master_send(client, rdata, nbytes+2); ++ if (xfer != nbytes+2) { ++ dev_err(&client->dev, ++ "%s: result=%d addr=%02x, data=%02x\n", ++ __func__, ++ xfer, rdata[1], rdata[2]); ++ return -EIO; ++ } ++ ++ /* If we wrote to the nonvolatile region, wait 10msec for write cycle*/ ++ if (reg_base < ISL12024_CCR_BASE) { ++ unsigned char al0e[3] = { 0, ISL12024_REG_INT, 0 }; ++ ++ msleep(10); ++ ++ /* ...and set or clear the AL0E bit in the INT register */ ++ ++ /* Need to set RWEL again as the write has cleared it */ ++ xfer = i2c_master_send(client, rwel, 3); ++ if (xfer != 3) { ++ dev_err(&client->dev, ++ "%s: aloe rwel - %d\n", ++ __func__, ++ xfer); ++ return -EIO; ++ } ++ ++ if (alm_enable) ++ al0e[2] = ISL12024_INT_AL0E; ++ ++ xfer = i2c_master_send(client, al0e, 3); ++ if (xfer != 3) { ++ dev_err(&client->dev, ++ "%s: al0e - %d\n", ++ __func__, ++ xfer); ++ return -EIO; ++ } ++ ++ /* and wait 10msec again for this write to complete */ ++ msleep(10); ++ } ++ ++ /* disable further writes */ ++ if ((xfer = i2c_master_send(client, diswe, 3)) != 3) { ++ dev_err(&client->dev, "%s: diswe - %d\n", __func__, xfer); ++ return -EIO; ++ } ++ ++ return 0; ++} ++ ++ ++static int isl12024_fix_osc(struct i2c_client *client) ++{ ++ int err; ++ struct rtc_time tm; ++ ++ tm.tm_hour = tm.tm_min = tm.tm_sec = 0; ++ ++ err = isl12024_set_datetime(client, &tm, 0, ISL12024_CCR_BASE, 0); ++ if ( err < 0 ) ++ printk(KERN_ERR DRV_NAME ": Unable to restart the oscillator (%d)\n", err); ++ ++ return err; ++} ++ ++ ++static int isl12024_rtc_read_time(struct device *dev, struct rtc_time *tm) ++{ ++ return isl12024_read_time(to_i2c_client(dev), tm); ++ ++} ++ ++ ++static int isl12024_rtc_set_time(struct device *dev, struct rtc_time *tm) ++{ ++ return isl12024_set_datetime(to_i2c_client(dev), ++ tm, 1, ISL12024_CCR_BASE, 0); ++} ++ ++ ++static int ++isl12024_rtc_proc(struct device *dev, struct seq_file *seq) ++{ ++ ++ /* Nothing to do */ ++ ++ return 0; ++} ++ ++ ++static const struct rtc_class_ops isl12024_rtc_ops = { ++ .proc = isl12024_rtc_proc, ++ .read_time = isl12024_rtc_read_time, ++ .set_time = isl12024_rtc_set_time, ++}; ++ ++static int ++read_proc(char * page, char ** start, off_t off, int count, int * eof, void * data) ++{ ++ int len = 0; ++ int i; ++ ++ for (i = 0; i < ISL12024_RTC_SECTION_LEN; i++) ++ len += sprintf(page+len, "%02X", buf_id[i]); ++ len += sprintf(page+len, "\n"); ++ ++ len -= off; ++ if ( len < count ) { ++ *eof = 1; ++ if ( len <= 0 ) ++ return 0; ++ } else { ++ len = count; ++ } ++ ++ *start = page + off; ++ ++ return len; ++} ++ ++ ++static int ++isl12024_probe(struct i2c_adapter *adapter, int addr, int kind) ++{ ++ int rc = 0; ++ int err = 0; ++ unsigned char sr; ++ struct i2c_client *new_client = NULL; ++ struct rtc_device *rtc = NULL; ++ struct proc_dir_entry *proc_root; ++ struct proc_dir_entry *proc_entry; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { ++ rc = -ENODEV; ++ goto failout; ++ } ++ ++ new_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL); ++ if (new_client == NULL) { ++ rc = -ENOMEM; ++ goto failout; ++ } ++ ++ new_client->addr = addr; ++ new_client->adapter = adapter; ++ new_client->driver = &isl12024_driver; ++ new_client->flags = 0; ++ strcpy(new_client->name, DRV_NAME); ++ ++ if (kind < 0) { ++ rc = isl12024_i2c_validate_client(new_client); ++ if (rc < 0) ++ goto failout; ++ } ++ ++ rc = i2c_attach_client(new_client); ++ if (rc < 0) ++ goto failout; ++ ++ rtc = rtc_device_register(isl12024_driver.driver.name, ++ &new_client->dev, ++ &isl12024_rtc_ops, THIS_MODULE); ++ ++ if (IS_ERR(rtc)) { ++ printk(KERN_ERR DRV_NAME ": Error during rtc registration\n"); ++ rc = PTR_ERR(rtc); ++ goto failout; ++ } ++ ++ i2c_set_clientdata(new_client, rtc); ++ ++ /* Check for power failures and eventualy enable the osc */ ++ if ((err = isl12024_get_status(new_client, &sr)) == 0) { ++ if (sr & ISL12024_SR_RTCF) { ++ printk(KERN_INFO DRV_NAME ": Power failure detected, please set the clock\n"); ++ udelay(50); ++ isl12024_fix_osc(new_client); ++ } ++ } ++ else { ++ printk(KERN_ERR DRV_NAME ": Couldn't read status\n"); ++ } ++ ++ proc_root = proc_mkdir(DRV_NAME, 0); ++ proc_entry = create_proc_entry("id", S_IFREG | S_IRUGO, proc_root); ++ if (proc_entry == NULL) ++ return -1; ++ ++ proc_entry->owner = THIS_MODULE; ++ proc_entry->read_proc = read_proc; ++ ++ /* Read unique id from eeprom */ ++ isl12024_i2c_read_regs(new_client, ISL12024_REG_ID, buf_id, sizeof(buf_id)); ++ ++ return 0; ++ ++ failout: ++ kfree(new_client); ++ return rc; ++} ++ ++ ++static int ++isl12024_attach_adapter (struct i2c_adapter *adapter) ++{ ++ return i2c_probe(adapter, &addr_data, isl12024_probe); ++} ++ ++ ++static int ++isl12024_detach_client(struct i2c_client *client) ++{ ++ int rc; ++ struct rtc_device *const rtc = i2c_get_clientdata(client); ++ ++ if (rtc) ++ rtc_device_unregister(rtc); ++ ++ rc = i2c_detach_client(client); ++ if (rc) ++ return rc; ++ ++ kfree(client); ++ ++ return 0; ++} ++ ++ ++/* module init/exit */ ++ ++static int __init isl12024_init(void) ++{ ++ return i2c_add_driver(&isl12024_driver); ++} ++ ++static void __exit isl12024_exit(void) ++{ ++ i2c_del_driver(&isl12024_driver); ++} ++ ++MODULE_AUTHOR("Guillaume Ligneul "); ++MODULE_DESCRIPTION("Intersil ISL12024 driver"); ++MODULE_LICENSE("GPL"); ++MODULE_VERSION(DRV_VERSION); ++ ++module_init(isl12024_init); ++module_exit(isl12024_exit); diff --git a/recipes/linux/linux-2.6.29/boc01/007-081217-lm73.patch b/recipes/linux/linux-2.6.29/boc01/007-081217-lm73.patch new file mode 100644 index 0000000000..ec2a4e40e8 --- /dev/null +++ b/recipes/linux/linux-2.6.29/boc01/007-081217-lm73.patch @@ -0,0 +1,269 @@ +Index: linux-2.6.29/drivers/hwmon/Kconfig +=================================================================== +--- linux-2.6.29.orig/drivers/hwmon/Kconfig 2009-03-24 00:12:14.000000000 +0100 ++++ linux-2.6.29/drivers/hwmon/Kconfig 2009-04-01 17:37:50.000000000 +0200 +@@ -448,6 +448,15 @@ + This driver can also be built as a module. If so, the module + will be called lm70. + ++config SENSORS_LM73 ++ tristate "National Semiconductor LM73" ++ depends on I2C ++ help ++ If you say yes here you get support for National Semiconductor LM73 ++ sensor chips ++ This driver can also be built as a module. If so, the module ++ will be called lm73. ++ + config SENSORS_LM75 + tristate "National Semiconductor LM75 and compatibles" + depends on I2C +Index: linux-2.6.29/drivers/hwmon/lm73.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.29/drivers/hwmon/lm73.c 2009-04-01 17:37:50.000000000 +0200 +@@ -0,0 +1,232 @@ ++ /* ++ * LM73 Sensor driver ++ * Based on LM75 ++ * ++ * ++ * Copyright (C) 2007, CenoSYS (www.cenosys.com). ++ * Guillaume Ligneul ++ * Guillaume.ligneul@gmail.com ++ * ++ * This software program is licensed subject to the GNU General Public License ++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++/* Addresses to scan */ ++static const unsigned short normal_i2c[] = { 0x48, 0x49, 0x4a, 0x4b, 0x4c, ++ 0x4d, 0x4e, 0x4f, I2C_CLIENT_END }; ++ ++/* Insmod parameters */ ++I2C_CLIENT_INSMOD_1(lm73); ++ ++/* LM73 registers */ ++#define LM73_REG_INPUT 0x00 ++#define LM73_REG_CONF 0x01 ++#define LM73_REG_T_HIGH 0x02 ++#define LM73_REG_T_LOW 0x03 ++ ++static const u8 LM73_REG_TEMP[3] = { ++ LM73_REG_INPUT, /* input */ ++ LM73_REG_T_HIGH, /* max */ ++ LM73_REG_T_LOW, /* min */ ++}; ++ ++/* Each client has this additional data */ ++struct lm73_data { ++ struct i2c_client client; ++ struct device *hwmon_dev; ++ struct mutex update_lock; ++ char valid; /* !=0 if following fields are valid */ ++ unsigned long last_updated; /* In jiffies */ ++ u16 temp[3]; /* Register values, ++ 0 = input ++ 1 = max ++ 2 = min */ ++}; ++ ++static int lm73_attach_adapter(struct i2c_adapter *adapter); ++static int lm73_detect(struct i2c_adapter *adapter, int address, int kind); ++static int lm73_detach_client(struct i2c_client *client); ++static int lm73_read_value(struct i2c_client *client, u8 reg); ++static int lm73_write_value(struct i2c_client *client, u8 reg, short value); ++ ++/* This is the driver that will be inserted */ ++static struct i2c_driver lm73_driver = { ++ .driver = { ++ .name = "lm73", ++ }, ++ .attach_adapter = lm73_attach_adapter, ++ .detach_client = lm73_detach_client, ++}; ++ ++static ssize_t show_temp(struct device *dev, struct device_attribute *da, ++ char *buf) ++{ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); ++ struct i2c_client *client = to_i2c_client(dev); ++ int iTemp = 0; ++ ++ iTemp = lm73_read_value(client, LM73_REG_TEMP[attr->index]); ++ ++ return sprintf(buf, "%d\n", iTemp); ++ ++ ++} ++ ++static ssize_t set_temp(struct device *dev, struct device_attribute *da, ++ const char *buf, size_t count) ++{ ++ struct sensor_device_attribute *attr = to_sensor_dev_attr(da); ++ struct i2c_client *client = to_i2c_client(dev); ++ int nr = attr->index; ++ ++ long tmp = simple_strtol(buf, NULL, 10); ++ ++ lm73_write_value(client, LM73_REG_TEMP[nr], tmp); ++ return count; ++} ++ ++static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, ++ show_temp, set_temp, 1); ++static SENSOR_DEVICE_ATTR(temp1_min, S_IWUSR | S_IRUGO, ++ show_temp, set_temp, 2); ++static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL, 0); ++ ++static int lm73_attach_adapter(struct i2c_adapter *adapter) ++{ ++ if (!(adapter->class & I2C_CLASS_HWMON)) ++ return 0; ++ ++ return i2c_probe(adapter, &addr_data, lm73_detect); ++} ++ ++static struct attribute *lm73_attributes[] = { ++ &sensor_dev_attr_temp1_input.dev_attr.attr, ++ &sensor_dev_attr_temp1_max.dev_attr.attr, ++ &sensor_dev_attr_temp1_min.dev_attr.attr, ++ ++ NULL ++}; ++ ++static const struct attribute_group lm73_group = { ++ .attrs = lm73_attributes, ++}; ++ ++/* This function is called by i2c_probe */ ++static int lm73_detect(struct i2c_adapter *adapter, int address, int kind) ++{ ++ struct i2c_client *new_client; ++ struct lm73_data *data; ++ int err = 0; ++ const char *name = ""; ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | ++ I2C_FUNC_SMBUS_WORD_DATA)) ++ goto exit; ++ ++ if (!(data = kzalloc(sizeof(struct lm73_data), GFP_KERNEL))) { ++ err = -ENOMEM; ++ goto exit; ++ } ++ ++ new_client = &data->client; ++ i2c_set_clientdata(new_client, data); ++ new_client->addr = address; ++ new_client->adapter = adapter; ++ new_client->driver = &lm73_driver; ++ new_client->flags = 0; ++ ++ name = "lm73"; ++ ++ /* Fill in the remaining client fields and put it into the global list */ ++ strlcpy(new_client->name, name, I2C_NAME_SIZE); ++ data->valid = 0; ++ mutex_init(&data->update_lock); ++ ++ /* Tell the I2C layer a new client has arrived */ ++ if ((err = i2c_attach_client(new_client))) ++ goto exit_free; ++ ++ /* Register sysfs hooks */ ++ if ((err = sysfs_create_group(&new_client->dev.kobj, &lm73_group))) ++ goto exit_detach; ++ ++ data->hwmon_dev = hwmon_device_register(&new_client->dev); ++ if (IS_ERR(data->hwmon_dev)) { ++ err = PTR_ERR(data->hwmon_dev); ++ goto exit_remove; ++ } ++ ++ return 0; ++ ++exit_remove: ++ sysfs_remove_group(&new_client->dev.kobj, &lm73_group); ++exit_detach: ++ i2c_detach_client(new_client); ++exit_free: ++ kfree(data); ++exit: ++ return err; ++} ++ ++static int lm73_detach_client(struct i2c_client *client) ++{ ++ struct lm73_data *data = i2c_get_clientdata(client); ++ hwmon_device_unregister(data->hwmon_dev); ++ sysfs_remove_group(&client->dev.kobj, &lm73_group); ++ i2c_detach_client(client); ++ kfree(data); ++ return 0; ++} ++ ++static int lm73_read_value(struct i2c_client *client, u8 reg) ++{ ++ short sVal; ++ ++ if (reg == LM73_REG_CONF) ++ return i2c_smbus_read_byte_data(client, reg); ++ else ++ { ++ sVal = swab16(i2c_smbus_read_word_data(client, reg)); ++ sVal = sVal >> 7; ++ return sVal; ++ } ++} ++ ++static int lm73_write_value(struct i2c_client *client, u8 reg, short value) ++{ ++ if (reg == LM73_REG_CONF) ++ return i2c_smbus_write_byte_data(client, reg, value); ++ else ++ { ++ value = value<<7; ++ return i2c_smbus_write_word_data(client, reg, swab16(value)); ++ } ++} ++ ++static int __init sensors_lm73_init(void) ++{ ++ return i2c_add_driver(&lm73_driver); ++} ++ ++static void __exit sensors_lm73_exit(void) ++{ ++ i2c_del_driver(&lm73_driver); ++} ++ ++MODULE_AUTHOR("Ligneul Guillaume "); ++MODULE_DESCRIPTION("LM73 driver"); ++MODULE_LICENSE("GPL"); ++ ++module_init(sensors_lm73_init); ++module_exit(sensors_lm73_exit); +Index: linux-2.6.29/drivers/hwmon/Makefile +=================================================================== +--- linux-2.6.29.orig/drivers/hwmon/Makefile 2009-03-24 00:12:14.000000000 +0100 ++++ linux-2.6.29/drivers/hwmon/Makefile 2009-04-01 17:37:50.000000000 +0200 +@@ -54,6 +54,7 @@ + obj-$(CONFIG_SENSORS_LIS3LV02D) += lis3lv02d.o hp_accel.o + obj-$(CONFIG_SENSORS_LM63) += lm63.o + obj-$(CONFIG_SENSORS_LM70) += lm70.o ++obj-$(CONFIG_SENSORS_LM73) += lm73.o + obj-$(CONFIG_SENSORS_LM75) += lm75.o + obj-$(CONFIG_SENSORS_LM77) += lm77.o + obj-$(CONFIG_SENSORS_LM78) += lm78.o diff --git a/recipes/linux/linux-2.6.29/boc01/008-081208-spi.patch b/recipes/linux/linux-2.6.29/boc01/008-081208-spi.patch new file mode 100644 index 0000000000..9c9e402ea3 --- /dev/null +++ b/recipes/linux/linux-2.6.29/boc01/008-081208-spi.patch @@ -0,0 +1,147 @@ +Index: linux-2.6.29/arch/powerpc/platforms/83xx/mpc831x_rdb.c +=================================================================== +--- linux-2.6.29.orig/arch/powerpc/platforms/83xx/mpc831x_rdb.c 2009-03-24 00:12:14.000000000 +0100 ++++ linux-2.6.29/arch/powerpc/platforms/83xx/mpc831x_rdb.c 2009-04-01 17:37:53.000000000 +0200 +@@ -15,17 +15,87 @@ + + #include + #include ++#include + + #include + #include + #include + #include ++#include ++#include + + #include "mpc83xx.h" + + /* + * Setup the architecture + */ ++struct gpio { ++ __be32 gpdir; ++ __be32 gpodr; ++ __be32 gpdat; ++ __be32 gpier; ++ __be32 gpimr; ++ __be32 gpicr; ++} __attribute__ ((packed)); ++static struct gpio *gpio_regs; ++ ++static void mpc83xx_spi_activate_cs(u8 cs, u8 polarity) ++{ ++ //printk(KERN_INFO "%s %d %d\n", __func__, cs, polarity); ++ if (polarity) ++ setbits32(&gpio_regs->gpdat, 1 << (31 - 14)); ++ else ++ clrbits32(&gpio_regs->gpdat, 1 << (31 - 14)); ++} ++ ++static void mpc83xx_spi_deactivate_cs(u8 cs, u8 polarity) ++{ ++//printk(KERN_INFO "%s %d %d\n", __func__, cs, polarity); ++ if (polarity) ++ clrbits32(&gpio_regs->gpdat, 1 << (31 - 14)); ++ else ++ setbits32(&gpio_regs->gpdat, 1 << (31 - 14)); ++ ++} ++ ++//static struct mmc_spi_platform_data mpc8313_mmc_pdata = { ++// .ocr_mask = MMC_VDD_33_34, ++//}; ++ ++static struct spi_board_info mpc8313_spi_boardinfo = { ++ .bus_num = 0x7000, ++ .chip_select = 0, ++ .max_speed_hz = 50000000, ++ .modalias = "spidev", ++// .platform_data = &mpc8313_mmc_pdata, ++}; ++ ++static int __init mpc8313_spi_init(void) ++{ ++ __be32 __iomem *psicrl; ++ ++ /* System I/O Configuration Register Low */ ++ psicrl = ioremap(get_immrbase() + MPC83XX_SICRL_OFFS, 0x4); ++ gpio_regs = ioremap(get_immrbase() + 0xc00, 0x20); ++ if (!psicrl || !gpio_regs) ++ return -ENOMEM; ++ ++ clrbits32(psicrl, 0x03F00000); ++ setbits32(psicrl, 0x30000000); ++ iounmap(psicrl); ++ ++ /* set GPIO13 as output */ ++ setbits32(&gpio_regs->gpdir, 1 << (31 - 14)); ++ clrbits32(&gpio_regs->gpodr, 1 << (31 - 14)); ++ setbits32(&gpio_regs->gpdat, 1 << (31 - 14)); ++ ++ return fsl_spi_init(&mpc8313_spi_boardinfo, 1, ++ mpc83xx_spi_activate_cs, ++ mpc83xx_spi_deactivate_cs); ++} ++ ++device_initcall(mpc8313_spi_init); ++ + static void __init mpc831x_rdb_setup_arch(void) + { + #ifdef CONFIG_PCI +@@ -40,6 +110,8 @@ + mpc83xx_add_bridge(np); + #endif + mpc831x_usb_cfg(); ++ ++ mpc8313_spi_init(); + } + + static void __init mpc831x_rdb_init_IRQ(void) +Index: linux-2.6.29/drivers/spi/spi_mpc83xx.c +=================================================================== +--- linux-2.6.29.orig/drivers/spi/spi_mpc83xx.c 2009-03-24 00:12:14.000000000 +0100 ++++ linux-2.6.29/drivers/spi/spi_mpc83xx.c 2009-04-01 17:37:53.000000000 +0200 +@@ -280,7 +280,9 @@ + if (pm) + pm--; + +- cs->hw_mode |= SPMODE_PM(pm); ++ cs->hw_mode = 0x0F700000; ++ mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode,cs->hw_mode); ++ + regval = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode); + if (cs->hw_mode != regval) { + unsigned long flags; +@@ -448,7 +450,7 @@ + cs->hw_mode = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode); + /* mask out bits we are going to set */ + cs->hw_mode &= ~(SPMODE_CP_BEGIN_EDGECLK | SPMODE_CI_INACTIVEHIGH +- | SPMODE_REV | SPMODE_LOOP); ++ | SPMODE_REV ); + + if (spi->mode & SPI_CPHA) + cs->hw_mode |= SPMODE_CP_BEGIN_EDGECLK; +@@ -456,8 +458,10 @@ + cs->hw_mode |= SPMODE_CI_INACTIVEHIGH; + if (!(spi->mode & SPI_LSB_FIRST)) + cs->hw_mode |= SPMODE_REV; +- if (spi->mode & SPI_LOOP) +- cs->hw_mode |= SPMODE_LOOP; ++ ++ cs->hw_mode = 0x0F700000; ++ mpc83xx_spi_write_reg(&mpc83xx_spi->base->mode,cs->hw_mode); ++ cs->hw_mode = mpc83xx_spi_read_reg(&mpc83xx_spi->base->mode); + + retval = mpc83xx_spi_setup_transfer(spi, NULL); + if (retval < 0) { +@@ -637,7 +641,7 @@ + INIT_LIST_HEAD(&mpc83xx_spi->queue); + + mpc83xx_spi->workqueue = create_singlethread_workqueue( +- master->dev.parent->bus_id); ++ dev->dev.bus_id); + if (mpc83xx_spi->workqueue == NULL) { + ret = -EBUSY; + goto free_irq; diff --git a/recipes/linux/linux-2.6.29/boc01/011-090115-gpio.patch b/recipes/linux/linux-2.6.29/boc01/011-090115-gpio.patch new file mode 100644 index 0000000000..fd20dccea3 --- /dev/null +++ b/recipes/linux/linux-2.6.29/boc01/011-090115-gpio.patch @@ -0,0 +1,440 @@ +Index: linux-2.6.29/drivers/char/Kconfig +=================================================================== +--- linux-2.6.29.orig/drivers/char/Kconfig 2009-03-24 00:12:14.000000000 +0100 ++++ linux-2.6.29/drivers/char/Kconfig 2009-04-01 17:37:55.000000000 +0200 +@@ -1020,6 +1020,24 @@ + tristate "NEC VR4100 series General-purpose I/O Unit support" + depends on CPU_VR41XX + ++config GPIO_MPC8313 ++ tristate "mpc8313e gpio" ++ depends on PPC_MPC831x ++ select INPUT ++ default y ++ help ++ Give userspace access to the GPIO pins on the MPC8313E devices. ++ ++config EXIO_MPC8313 ++ tristate "mpc8313e exio" ++ depends on PPC_MPC831x ++ select INPUT ++ select LEDS_CLASS ++ default y ++ help ++ Give userspace access to the Extenrder IO pins on the CPE board. ++ ++ + config RAW_DRIVER + tristate "RAW driver (/dev/raw/rawN)" + depends on BLOCK +Index: linux-2.6.29/drivers/char/Makefile +=================================================================== +--- linux-2.6.29.orig/drivers/char/Makefile 2009-03-24 00:12:14.000000000 +0100 ++++ linux-2.6.29/drivers/char/Makefile 2009-04-01 17:37:55.000000000 +0200 +@@ -109,6 +109,8 @@ + obj-$(CONFIG_PS3_FLASH) += ps3flash.o + + obj-$(CONFIG_JS_RTC) += js-rtc.o ++obj-$(CONFIG_GPIO_MPC8313) += mpc8313e_gpio.o ++obj-$(CONFIG_EXIO_MPC8313) += mpc8313e_exio.o + js-rtc-y = rtc.o + + # Files generated that shall be removed upon make clean +Index: linux-2.6.29/drivers/char/mpc8313e_exio.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.29/drivers/char/mpc8313e_exio.c 2009-04-01 17:37:55.000000000 +0200 +@@ -0,0 +1,240 @@ ++/* ++* CPE Extender io driver ++* ++* Copyright (C) 2007, CenoSYS (www.cenosys.com). ++* ++* Alexandre Coffignal ++* Sylvain Giroudon ++* ++* This software program is licensed subject to the GNU General Public License ++* (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html ++* ++* Allows a user space process to control the EXIO pins. ++* ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static char module_name[] = "exio"; ++ ++ ++#define NB_EXIO 8 ++#define DEFAULT_STATE 0x58 ++#define EXIO_BASE 0xfa000000 ++#define EXIO_SIZE 0x2 ++#define EXIO_LED_MASK 0x01 ++ ++static int major = 0; ++static u8 exio_state = DEFAULT_STATE; ++static void *exio_io = NULL; ++static struct resource *exio_mem = NULL; ++static struct class * exio_class = NULL; ++ ++ ++static void ++exio_led_set(struct led_classdev *led_cdev, ++ enum led_brightness value) ++{ ++ if ( value ) ++ exio_state &= ~EXIO_LED_MASK; ++ else ++ exio_state |= EXIO_LED_MASK; ++ ++ iowrite8(exio_state, exio_io); ++} ++ ++static struct led_classdev exio_led = { ++ .name = "boc:blue:status", ++ .brightness_set = exio_led_set, ++}; ++ ++static inline int ++exio_led_init(void) ++{ ++ return led_classdev_register(NULL, &exio_led); ++} ++ ++ ++static inline void ++exio_led_exit(void) ++{ ++ led_classdev_unregister(&exio_led); ++} ++ ++ ++static inline void ++exio_led_suspend(void) ++{ ++ led_classdev_suspend(&exio_led); ++} ++ ++ ++static inline void ++exio_led_resume(void) ++{ ++ led_classdev_resume(&exio_led); ++} ++ ++ ++static ssize_t exio_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) ++{ ++ unsigned m = iminor(file->f_path.dentry->d_inode); ++ size_t i; ++ char mask; ++ int err = 0; ++ ++ for (i = 0; i < len; ++i) { ++ char c; ++ if (get_user(c, data + i)) ++ return -EFAULT; ++ ++ mask=(1<<(7-m)); ++ switch (c) { ++ case '0': ++ /*Clear exio level */ ++ exio_state&=~mask; ++ iowrite8(exio_state, exio_io); ++ break; ++ case '1': ++ /*Set exio level */ ++ exio_state|=mask; ++ iowrite8(exio_state, exio_io); ++ break; ++ default: ++ printk(KERN_DEBUG "exio%2d bad setting: chr<0x%2x>\n", ++ m, (int)c); ++ err++; ++ } ++ } ++ if (err) ++ return -EINVAL; ++ ++ return len; ++} ++ ++ ++static ssize_t exio_read(struct file *file, char __user * buf, ++ size_t len, loff_t * ppos) ++{ ++ unsigned m = iminor(file->f_path.dentry->d_inode); ++ int value; ++ char mask; ++ char state=ioread8(exio_io); ++ ++ mask=(1<<(7-m)); ++ value=state&mask; ++ if (put_user(value ? '1' : '0', buf)) ++ return -EFAULT; ++ return 1; ++ ++} ++ ++static int exio_open(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static int exio_close(struct inode *inode, struct file *file) ++{ ++ printk(KERN_DEBUG "close()\n"); ++ return 0; ++} ++ ++struct file_operations exio_fops = ++{ ++ .owner = THIS_MODULE, ++ .read = exio_read, ++ .write = exio_write, ++ .open = exio_open, ++ .release = exio_close /* correspond a close */ ++}; ++ ++ ++static void exio_cleanup(void) ++{ ++ if ( exio_mem ) ++ release_mem_region(EXIO_BASE, EXIO_SIZE); ++ exio_mem = NULL; ++ exio_io = NULL; ++ ++ if ( exio_class ) ++ class_destroy(exio_class); ++ exio_class = NULL; ++ ++ unregister_chrdev(major, module_name); ++} ++ ++ ++static int __init exio_init(void) ++{ ++ int rc, i; ++ ++ rc = register_chrdev(major, module_name, &exio_fops); ++ if ( rc < 0 ) ++ return rc; ++ ++ if ( major == 0 ) { ++ major = rc; /* accept dynamic major number */ ++ printk(KERN_INFO "%s: successfully loaded with major %d\n",module_name, major); ++ } ++ ++ exio_class = class_create(THIS_MODULE, "exio"); ++ if ( exio_class == NULL ) ++ goto out_cleanup; ++ ++ for (i = 0; i < NB_EXIO; i++) { ++ device_create(exio_class, NULL, MKDEV(major, i) ,NULL, "exio%i", i); ++ } ++ ++ /* System I/O Configuration Register Low */ ++ exio_mem = request_mem_region(EXIO_BASE, EXIO_SIZE, "exio"); ++ if ( exio_mem == NULL ) ++ goto out_cleanup; ++ ++ exio_io = ioremap(EXIO_BASE, EXIO_SIZE); ++ if ( exio_io == NULL ) ++ goto out_cleanup; ++ ++ /* Output default exio state */ ++ iowrite8(exio_state, exio_io); ++ ++ /* Register led class entry for driving the Status led */ ++ exio_led_init(); ++ ++ return 0; ++ ++out_cleanup: ++ exio_cleanup(); ++ return -ENOMEM; ++} ++ ++static void __exit exio_exit(void) ++{ ++ /* Unregister Status led */ ++ exio_led_exit(); ++ ++ /* Cleanup all other gears */ ++ exio_cleanup(); ++} ++ ++ ++module_param(major, int, 0644); ++MODULE_PARM_DESC(major, "Static major number (none = dynamic)"); ++MODULE_AUTHOR("Alexandre Coffignal "); ++MODULE_DESCRIPTION("boc01 exio management"); ++MODULE_LICENSE("GPL"); ++ ++module_init(exio_init); ++module_exit(exio_exit); ++ ++ +Index: linux-2.6.29/drivers/char/mpc8313e_gpio.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.29/drivers/char/mpc8313e_gpio.c 2009-04-01 17:37:55.000000000 +0200 +@@ -0,0 +1,148 @@ ++/* ++* mpc8313e gpio driver ++* ++* ++* Copyright (C) 2007, CenoSYS (www.cenosys.com). ++* Alexandre Coffignal ++* alexandre.coffignal@cenosys.com ++* ++* This software program is licensed subject to the GNU General Public License ++* (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html ++* ++* Allows a user space process to control the GPIO pins. ++* ++*/ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++static char module_name[] = "gpio"; ++#define NB_GPIO 8 ++static int major = 0; ++struct gpio { ++ __be32 gpdir; ++ __be32 gpodr; ++ __be32 gpdat; ++ __be32 gpier; ++ __be32 gpimr; ++ __be32 gpicr; ++} __attribute__ ((packed)); ++static struct gpio *gpio_regs; ++ ++static ssize_t mpc8313e_gpio_write(struct file *file, const char __user *data, size_t len, loff_t *ppos) ++{ ++ unsigned m = iminor(file->f_path.dentry->d_inode); ++ size_t i; ++ int err = 0; ++ ++ for (i = 0; i < len; ++i) { ++ char c; ++ if (get_user(c, data + i)) ++ return -EFAULT; ++ /* set GPIO as output */ ++ setbits32(&gpio_regs->gpdir, 1 << (31 - m)); ++ clrbits32(&gpio_regs->gpodr, 1 << (31 - m)); ++ switch (c) { ++ case '0': ++ /*Set GPIO level */ ++ clrbits32(&gpio_regs->gpdat, 1 << (31 - m)); ++ break; ++ case '1': ++ /*Set GPIO level */ ++ setbits32(&gpio_regs->gpdat, 1 << (31 - m)); ++ break; ++ default: ++ printk(KERN_DEBUG "io%2d bad setting: chr<0x%2x>\n", ++ m, (int)c); ++ err++; ++ } ++ } ++ if (err) ++ return -EINVAL; ++ ++ return len; ++} ++ ++static ssize_t mpc8313e_gpio_read(struct file *file, char __user * buf, size_t len, loff_t * ppos) ++{ ++ unsigned m = iminor(file->f_path.dentry->d_inode); ++ int value; ++ value=in_be32(&gpio_regs->gpdat)&(1 << (31 - m)); ++ if (put_user(value ? '1' : '0', buf)) ++ return -EFAULT; ++ return 1; ++ ++ ++} ++ ++ ++ ++static int mpc8313e_gpio_open(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++static int mpc8313e_gpio_close(struct inode *inode, struct file *file) ++{ ++ return 0; ++} ++ ++struct file_operations mpc8313e_gpio_fops = ++{ ++ .owner = THIS_MODULE, ++ .read = mpc8313e_gpio_read, ++ .write = mpc8313e_gpio_write, ++ .open = mpc8313e_gpio_open, ++ .release = mpc8313e_gpio_close ++}; ++static struct class * gpio_class; ++static int __init mpc8313e_gpio_init(void) ++{ ++ int rc,i; ++ ++ rc = register_chrdev(major, module_name, &mpc8313e_gpio_fops); ++ if (rc < 0) { ++ return rc; ++ } ++ ++ if (major == 0) { ++ major = rc; /* accept dynamic major number */ ++ printk(KERN_INFO "%s: successfully loaded with major %d\n",module_name, major); ++ ++ } ++ gpio_class = class_create(THIS_MODULE, "gpio"); ++ ++ for (i = 0; i < NB_GPIO; i++) ++ { ++ device_create(gpio_class, NULL, MKDEV(major, i) ,NULL, "gpio%d",i); ++ ++ } ++ ++ /* System I/O Configuration Register Low */ ++ gpio_regs = ioremap(get_immrbase() + 0xc00, 0x20); ++ if (!gpio_regs) ++ return -ENOMEM; ++ return 0; ++} ++ ++static void __exit mpc8313e_gpio_cleanup(void) ++{ ++ unregister_chrdev(major, module_name); ++} ++module_param(major, int, 0644); ++MODULE_PARM_DESC(major, "Static major number (none = dynamic)"); ++MODULE_AUTHOR("Alexandre Coffignal "); ++MODULE_DESCRIPTION("mpc8313e GPIO"); ++MODULE_LICENSE("GPL"); ++ ++module_init(mpc8313e_gpio_init); ++module_exit(mpc8313e_gpio_cleanup); ++ ++ diff --git a/recipes/linux/linux-2.6.29/boc01/012-090219-capsense.patch b/recipes/linux/linux-2.6.29/boc01/012-090219-capsense.patch new file mode 100644 index 0000000000..ce4af93e3a --- /dev/null +++ b/recipes/linux/linux-2.6.29/boc01/012-090219-capsense.patch @@ -0,0 +1,883 @@ +Index: linux-2.6.29/drivers/input/misc/Kconfig +=================================================================== +--- linux-2.6.29.orig/drivers/input/misc/Kconfig 2009-03-24 00:12:14.000000000 +0100 ++++ linux-2.6.29/drivers/input/misc/Kconfig 2009-04-01 17:38:02.000000000 +0200 +@@ -227,4 +227,13 @@ + Say Y to include support for delivering PMU events via input + layer on NXP PCF50633. + ++config INPUT_CAPSENSE_BTNS ++ tristate "CAPSENSE CY8C201xx buttons interface" ++ select INPUT_POLLDEV ++ select LEDS_CLASS ++ help ++ To compile this driver as a module, choose M here: ++ the module will be called capsense-btns. ++ To change poll interval, invoque poll parameter in msecs. ++ + endif +Index: linux-2.6.29/drivers/input/misc/Makefile +=================================================================== +--- linux-2.6.29.orig/drivers/input/misc/Makefile 2009-03-24 00:12:14.000000000 +0100 ++++ linux-2.6.29/drivers/input/misc/Makefile 2009-04-01 17:38:23.000000000 +0200 +@@ -22,3 +22,4 @@ + obj-$(CONFIG_INPUT_APANEL) += apanel.o + obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o + obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o ++obj-$(CONFIG_INPUT_CAPSENSE_BTNS) += capsense-btns.o +Index: linux-2.6.29/drivers/input/misc/capsense-btns.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.29/drivers/input/misc/capsense-btns.c 2009-04-01 17:38:02.000000000 +0200 +@@ -0,0 +1,456 @@ ++/* ++ * CAPSENSE Interface driver ++ * ++ * ++ * Copyright (C) 2008, CenoSYS (www.cenosys.com). ++ * ++ * Guillaume Ligneul ++ * Jeremy Lainé ++ * Sylvain Giroudon ++ * ++ * This software program is licensed subject to the GNU General Public License ++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++ ++ ++static int capsense_attach_adapter(struct i2c_adapter *adapter); ++static int capsense_detach_client(struct i2c_client *client); ++#ifdef CONFIG_PM ++static int capsense_suspend(struct i2c_client *client, pm_message_t mesg); ++static int capsense_resume(struct i2c_client *client); ++#endif ++ ++#define DRIVER_NAME "capsense" ++ ++/* i2c configuration */ ++#define CAPSENSE_I2C_ADDR 0x25 ++// To debug (may be add in include/linux/i2c-id.h) ++#define I2C_DRIVERID_CAPSENSE 98 ++ ++#define BUTTONS_POLL_INTERVAL 30 /* msec */ ++ ++#define CAP_INPUT_PORT(port) (0x00+(port)) ++#define CAP_STATUS_PORT(port) (0x02+(port)) ++#define CAP_OUTPUT_PORT(port) (0x04+(port)) ++#define CAP_CS_ENABLE(port) (0x06+(port)) ++#define CAP_GPIO_ENABLE(port) (0x08+(port)) ++#define CAP_INVERSION_MASK(port) (0x0A+(port)) ++#define CAP_INT_MASK(port) (0x0C+(port)) ++#define CAP_STATUS_HOLD_MSK(port) (0x0E+(port)) ++#define CAP_DM_PULL_UP(port) (0x10+(4*(port))) ++#define CAP_DM_STRONG(port) (0x11+(4*(port))) ++#define CAP_DM_HIGHZ(port) (0x12+(4*(port))) ++#define CAP_OD_LOW(port) (0x13+(4*(port))) ++#define CAP_PWM_ENABLE(port) (0x18+(port)) ++#define CAP_PWM_MODE_DC 0x1A ++#define CAP_PWM_DELAY 0x1B ++#define CAP_OP_SEL(port,bit) (0x1C+(25*(port))+(5*(bit))) ++#define CAP_READ_STATUS(port) (0x88+(port)) ++ ++#define CAP_CS(command,port,bit) ((command)+(5*(port))+(bit)) ++#define CAP_CS_FINGER_TH(port,bit) CAP_CS(0x61,port,bit) ++#define CAP_CS_IDAC(port,bit) CAP_CS(0x6B,port,bit) ++ ++#define CAP_DEVICE_ID 0x7A ++#define CAP_DEVICE_STATUS 0x7B ++#define CAP_I2C_ADDR_DM 0x7C ++ ++#define CAP_COMMAND_REG 0xA0 ++#define CAP_COMMAND_STORE_NVM 0x01 ++#define CAP_COMMAND_RESTORE_FACTORY 0x02 ++#define CAP_COMMAND_RECONFIGURE 0x06 ++#define CAP_COMMAND_NORMAL_MODE 0x07 ++#define CAP_COMMAND_SETUP_MODE 0x08 ++ ++#define CAP_NLEDS 5 ++ ++static int poll_interval = BUTTONS_POLL_INTERVAL; ++module_param_named(poll, poll_interval, uint, 0); ++MODULE_PARM_DESC(poll, "poll interval in msec (30=default)"); ++ ++static const unsigned short normal_i2c[] = { ++ CAPSENSE_I2C_ADDR , I2C_CLIENT_END ++}; ++I2C_CLIENT_INSMOD; ++ ++static struct i2c_driver capsense_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ }, ++ .id = I2C_DRIVERID_CAPSENSE, ++ .attach_adapter = &capsense_attach_adapter, ++ .detach_client = &capsense_detach_client, ++#ifdef CONFIG_PM ++ .suspend = &capsense_suspend, ++ .resume = &capsense_resume, ++#endif ++}; ++ ++struct capsense_led { ++ struct led_classdev cdev; ++ struct capsense_ctx *capsense; ++ int port; ++ int bit; ++}; ++ ++struct capsense_ctx { ++ struct input_polled_dev *ipdev; ++ struct i2c_client client; ++ unsigned char key_state; ++ struct capsense_led leds[CAP_NLEDS]; ++ unsigned char led_state[2]; ++ struct mutex mutex; ++}; ++ ++static unsigned short input_keymap[] = { ++ // GP0 ++ KEY_F1, ++ KEY_ENTER, ++ KEY_DOWN, ++ KEY_BACKSPACE, ++ // GP1 ++ KEY_UP, ++}; ++ ++struct capsense_keymap { ++ char *name; ++ int port, bit; ++}; ++ ++static struct capsense_keymap phys_keymap[] = { ++ { "info", 0, 4 }, ++ { "ok", 0, 2 }, ++ { "down", 0, 3 }, ++ { "back", 0, 0 }, ++ { "up", 1, 4 }, ++}; ++ ++ ++struct capsense_ledmap { ++ char *name; ++ int port, bit; ++}; ++ ++static struct capsense_ledmap ledmap[CAP_NLEDS] = { ++ { "capsense:blue:back", 0, 1 }, ++ { "capsense:blue:info", 1, 0 }, ++ { "capsense:blue:down", 1, 1 }, ++ { "capsense:blue:ok", 1, 2 }, ++ { "capsense:blue:up", 1, 3 }, ++}; ++ ++ ++/* ++ * Buttons events handling ++ */ ++ ++static void handle_buttons(struct input_polled_dev *dev) ++{ ++ struct capsense_ctx *capsense = dev->private; ++ u8 port_value; ++ u8 new_state = 0; ++ int port; ++ u8 changed; ++ int i; ++ ++ mutex_lock(&capsense->mutex); ++ ++ // read status ++ port = -1; ++ port_value = 0; ++ for (i = 0; i < ARRAY_SIZE(phys_keymap); i++) { ++ if ( phys_keymap[i].port != port ) { ++ port = phys_keymap[i].port; ++ port_value = i2c_smbus_read_byte_data(&capsense->client, CAP_READ_STATUS(port)); ++ } ++ ++ if ( port_value & (1 << phys_keymap[i].bit) ) ++ new_state |= (1 << i); ++ } ++ ++ mutex_unlock(&capsense->mutex); ++ ++ // update keyboard state ++ changed = capsense->key_state ^ new_state; ++ for (i = 0; i < ARRAY_SIZE(input_keymap); i++) ++ if (changed & (1 << i)) ++ input_report_key(dev->input, input_keymap[i], (new_state & (1 << i))); ++ capsense->key_state = new_state; ++ input_sync(dev->input); ++} ++ ++ ++/* ++ * LEDs management ++ */ ++ ++static void ++capsense_led_set(struct led_classdev *led_cdev, ++ enum led_brightness value) ++{ ++ struct capsense_led *led = (struct capsense_led *) led_cdev; ++ struct capsense_ctx *capsense = led->capsense; ++ int port = led->port; ++ unsigned char mask = (1 << led->bit); ++ ++ if ( value ) ++ capsense->led_state[port] |= mask; ++ else ++ capsense->led_state[port] &= ~mask; ++ ++ mutex_lock(&capsense->mutex); ++ i2c_smbus_write_byte_data(&capsense->client, CAP_OUTPUT_PORT(port), capsense->led_state[port]); ++ mutex_unlock(&capsense->mutex); ++} ++ ++static void ++capsense_led_enable(struct capsense_led *led, int state) ++{ ++ struct capsense_ctx *capsense = led->capsense; ++ ++ mutex_lock(&capsense->mutex); ++ i2c_smbus_write_byte_data(&capsense->client, CAP_OP_SEL(led->port, led->bit), state ? 0x00 : 0x80); ++ mutex_unlock(&capsense->mutex); ++} ++ ++static int ++capsense_led_init(struct capsense_ctx *capsense) ++{ ++ int i; ++ int ret; ++ ++ for (i = 0; i < CAP_NLEDS; i++) { ++ struct capsense_led *led = &(capsense->leds[i]); ++ ++ led->cdev.name = ledmap[i].name; ++ led->cdev.brightness_set = capsense_led_set; ++ led->capsense = capsense; ++ led->port = ledmap[i].port; ++ led->bit = ledmap[i].bit; ++ ++ ret = led_classdev_register(&capsense->ipdev->input->dev, &led->cdev); ++ if ( ret < 0 ) ++ return -1; ++ ++ capsense_led_enable(led, 1); ++ } ++ ++ /* Switch all leds off */ ++ mutex_lock(&capsense->mutex); ++ ++ capsense->led_state[0] = 0x00; ++ i2c_smbus_write_byte_data(&capsense->client, CAP_OUTPUT_PORT(0), 0x00); ++ ++ capsense->led_state[1] = 0x00; ++ i2c_smbus_write_byte_data(&capsense->client, CAP_OUTPUT_PORT(1), 0x00); ++ ++ mutex_unlock(&capsense->mutex); ++ ++ return 0; ++} ++ ++ ++static void ++capsense_led_exit(struct capsense_ctx *capsense) ++{ ++ int i; ++ ++ for (i = 0; i < CAP_NLEDS; i++) { ++ led_classdev_unregister(&capsense->leds[i].cdev); ++ } ++} ++ ++ ++static inline void ++capsense_led_suspend(struct capsense_ctx *capsense) ++{ ++ int i; ++ ++ for (i = 0; i < CAP_NLEDS; i++) { ++ struct capsense_led *led = &(capsense->leds[i]); ++ ++ led_classdev_suspend(&led->cdev); ++ capsense_led_enable(led, 0); ++ } ++} ++ ++ ++static inline void ++capsense_led_resume(struct capsense_ctx *capsense) ++{ ++ int i; ++ ++ for (i = 0; i < CAP_NLEDS; i++) { ++ struct capsense_led *led = &(capsense->leds[i]); ++ ++ capsense_led_enable(led, 1); ++ led_classdev_resume(&led->cdev); ++ } ++} ++ ++ ++/* ++ * Device configuration through procfs entries ++ */ ++ ++#ifdef CONFIG_PROC_FS ++#include "capsense-procfs.c" ++#endif ++ ++ ++/* ++ * Device initialisation ++ */ ++ ++static int ++capsense_probe(struct i2c_adapter *adapter, int addr, int kind) ++{ ++ struct capsense_ctx *capsense; ++ struct input_polled_dev *ipdev; ++ struct input_dev *input; ++ int rc = 0, err = -ENOMEM, i=0; ++ ++ capsense = kzalloc(sizeof(*capsense), GFP_KERNEL); ++ if (!capsense) ++ goto failout; ++ ++ mutex_init(&capsense->mutex); ++ ++ if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { ++ goto failout; ++ } ++ ++ ipdev = input_allocate_polled_device(); ++ if (!ipdev) ++ goto failout; ++ ++ capsense->key_state = 0; ++ capsense->ipdev = ipdev; ++ capsense->client.adapter = adapter; ++ capsense->client.addr = addr; ++ capsense->client.driver = &capsense_driver; ++ strlcpy(capsense->client.name, DRIVER_NAME, I2C_NAME_SIZE); ++ i2c_set_clientdata(&capsense->client, capsense); ++ ++ rc = i2c_attach_client(&capsense->client); ++ if (rc) ++ goto out_attach; ++ ++ ipdev->poll = handle_buttons; ++ ipdev->private = capsense; ++ ipdev->poll_interval = poll_interval; ++ ++ input = ipdev->input; ++ input->name = "Capsense buttons"; ++ input->phys = "capsense/input0"; ++ input->id.bustype = BUS_I2C; ++ input->dev.parent = &capsense->client.dev; ++ ++ input->keycode = input_keymap; ++ input->keycodemax = ARRAY_SIZE(input_keymap); ++ input->keycodesize = sizeof(unsigned short); ++ ++ input_set_capability(input, EV_MSC, MSC_SCAN); ++ set_bit(EV_KEY, ipdev->input->evbit); ++ ++ for (i = 0; i < ARRAY_SIZE(input_keymap); i++) ++ set_bit(input_keymap[i], ipdev->input->keybit); ++ ++ rc = input_register_polled_device(ipdev); ++ if(rc) ++ goto out_polled; ++ ++ if ( capsense_led_init(capsense) ) ++ goto out_registered; ++ ++#ifdef CONFIG_PROC_FS ++ /* Create /proc entries for hardware device configuration */ ++ capsense_proc_init(capsense); ++#endif ++ ++ return 0; ++ ++out_registered: ++ input_unregister_polled_device(ipdev); ++out_polled: ++ i2c_detach_client(&capsense->client); ++out_attach: ++ input_free_polled_device(ipdev); ++failout: ++ return err; ++} ++ ++static int ++capsense_attach_adapter (struct i2c_adapter *adapter) ++{ ++ return i2c_probe(adapter, &addr_data, capsense_probe); ++} ++ ++static int ++capsense_detach_client(struct i2c_client *client) ++{ ++ struct capsense_ctx *capsense = i2c_get_clientdata(client); ++ ++ capsense_led_exit(capsense); ++ input_unregister_polled_device(capsense->ipdev); ++ i2c_detach_client(&capsense->client); ++ input_free_polled_device(capsense->ipdev); ++ return 0; ++} ++ ++ ++/* ++ * Power management ++ */ ++ ++#ifdef CONFIG_PM ++static int capsense_suspend(struct i2c_client *client, pm_message_t mesg) ++{ ++ struct capsense_ctx *capsense = i2c_get_clientdata(client); ++ ++ printk(KERN_INFO DRIVER_NAME ": suspend\n"); ++ ++ capsense_led_suspend(capsense); ++ ++ return 0; ++} ++ ++static int capsense_resume(struct i2c_client *client) ++{ ++ struct capsense_ctx *capsense = i2c_get_clientdata(client); ++ ++ printk(KERN_INFO DRIVER_NAME ": resume\n"); ++ ++ capsense_led_resume(capsense); ++ ++ return 0; ++} ++#endif ++ ++ ++/* ++ * Driver initialisation ++ */ ++ ++static int __init capsense_buttons_init(void) ++{ ++ return i2c_add_driver(&capsense_driver); ++} ++ ++static void __exit capsense_buttons_exit(void) ++{ ++ i2c_del_driver(&capsense_driver); ++} ++ ++MODULE_AUTHOR("Guillaume Ligneul "); ++MODULE_DESCRIPTION("Capsense CY8C201xx Input driver"); ++MODULE_LICENSE("GPL"); ++module_init(capsense_buttons_init); ++module_exit(capsense_buttons_exit); +Index: linux-2.6.29/drivers/input/misc/capsense-procfs.c +=================================================================== +--- /dev/null 1970-01-01 00:00:00.000000000 +0000 ++++ linux-2.6.29/drivers/input/misc/capsense-procfs.c 2009-04-01 17:38:02.000000000 +0200 +@@ -0,0 +1,390 @@ ++/* ++ * CAPSENSE Interface driver ++ * Device setup using procfs ++ * ++ * Copyright (C) 2008, Goobie (www.goobie.fr). ++ * ++ * Sylvain Giroudon ++ * ++ * This software program is licensed subject to the GNU General Public License ++ * (GPL).Version 2,June 1991, available at http://www.fsf.org/copyleft/gpl.html ++ */ ++ ++#include ++#include ++ ++ ++struct capsense_proc_data { ++ struct capsense_ctx *capsense; ++ struct capsense_keymap *phys; ++ char *name; ++ unsigned char command; ++ unsigned char hex; ++}; ++ ++struct capsense_proc_entry { ++ char *name; ++ unsigned char command; ++ struct capsense_proc_data data[ARRAY_SIZE(phys_keymap)]; ++}; ++ ++static struct capsense_proc_entry capsense_proc_key_entries[] = { ++ { "CS_FINGER_TH", CAP_CS_FINGER_TH(0,0) }, ++ { "CS_IDAC", CAP_CS_IDAC(0,0) }, ++}; ++ ++static struct capsense_proc_entry capsense_proc_device_entries[] = { ++ { "DEVICE_ID", CAP_DEVICE_ID }, ++ { "DEVICE_STATUS", CAP_DEVICE_STATUS }, ++}; ++ ++struct capsense_proc_command { ++ char *name; ++ unsigned char command; ++ struct capsense_ctx *capsense; ++}; ++ ++static struct capsense_proc_command capsense_proc_commands[] = { ++ { "store", CAP_COMMAND_STORE_NVM }, ++ //{ "factory", CAP_COMMAND_RESTORE_FACTORY }, ++ //{ "reconfigure", CAP_COMMAND_RECONFIGURE }, ++}; ++ ++ ++static int capsense_proc_read(char *page, char **start, off_t off, int count, ++ int *eof, void *_data) ++{ ++ struct capsense_proc_data *data = _data; ++ struct capsense_ctx *capsense = data->capsense; ++ u8 value; ++ int len; ++ ++ mutex_lock(&capsense->mutex); ++ value = i2c_smbus_read_byte_data(&capsense->client, data->command); ++ mutex_unlock(&capsense->mutex); ++ ++ if ( data->hex ) ++ len = sprintf(page, "%02X\n", value); ++ else ++ len = sprintf(page, "%u\n", value); ++ ++ len -= off; ++ if ( len < count ) { ++ *eof = 1; ++ if ( len <= 0 ) ++ return 0; ++ } else { ++ len = count; ++ } ++ ++ *start = page + off; ++ ++ return len; ++} ++ ++ ++static int capsense_proc_write(struct file *file, const char *buf, ++ unsigned long count, void *_data) ++{ ++ struct capsense_proc_data *data = _data; ++ struct capsense_ctx *capsense = data->capsense; ++ char lbuf[count+1]; ++ u8 value; ++ ++ /* Only root can do this */ ++ if ( !capable(CAP_SYS_ADMIN) ) ++ return -EACCES; ++ ++ memset(lbuf, 0, sizeof(lbuf)); ++ ++ if (copy_from_user(lbuf, buf, count)) ++ return -EFAULT; ++ ++ if ( sscanf(lbuf, "%hhi", &value) == 1 ) { ++ mutex_lock(&capsense->mutex); ++ i2c_smbus_write_byte_data(&capsense->client, CAP_COMMAND_REG, CAP_COMMAND_SETUP_MODE); ++ i2c_smbus_write_byte_data(&capsense->client, data->command, value); ++ i2c_smbus_write_byte_data(&capsense->client, CAP_COMMAND_REG, CAP_COMMAND_NORMAL_MODE); ++ mutex_unlock(&capsense->mutex); ++ } ++ else { ++ printk(KERN_INFO DRIVER_NAME ": [%s/%s] Syntax error in expression\n", data->phys->name, data->name); ++ return -EINVAL; ++ } ++ ++ return count; ++} ++ ++ ++static inline char *str_skip_blanks(char *s) ++{ ++ while ( (*s != '\0') && (*s <= ' ') ) ++ s++; ++ return s; ++} ++ ++ ++static inline char *str_skip_chars(char *s) ++{ ++ while ( *s > ' ' ) ++ s++; ++ return s; ++} ++ ++ ++static int capsense_proc_write_iic(struct file *file, const char *buf, ++ unsigned long count, void *data) ++{ ++ struct capsense_ctx *capsense = data; ++ char lbuf[count+1]; ++ int lnum; ++ char *s; ++ ++ /* Only root can do this */ ++ if ( !capable(CAP_SYS_ADMIN) ) ++ return -EACCES; ++ ++ printk(KERN_INFO DRIVER_NAME ": Reading configuration script from /proc/" DRIVER_NAME "/iic (%lu bytes)\n", count); ++ ++ memset(lbuf, 0, sizeof(lbuf)); ++ ++ if (copy_from_user(lbuf, buf, count)) ++ return -EFAULT; ++ ++ s = lbuf; ++ lnum = 0; ++ ++ while ( *s != '\0' ) { ++ char *line; ++ char operation; ++ unsigned char data[255]; ++ int size; ++ struct i2c_msg msg; ++ int ret; ++ ++ lnum++; ++ ++ /* Spot the end of the line */ ++ line = s; ++ while ( (*s != '\0') && (*s != '\n') ) ++ s++; ++ if ( *s != '\0' ) ++ *(s++) = '\0'; ++ ++ //printk(KERN_INFO DRIVER_NAME ": iic:%d: '%s'\n", lnum, line); ++ ++ /* Strip leading blank characters */ ++ line = str_skip_blanks(line); ++ ++ /* Empty or commented line: skip! */ ++ if ( (*line == '\0') || (*line == '#') ) ++ continue; ++ ++ /* Only accept write operations 'w' */ ++ operation = *(line++); ++ if ( operation != 'w' ) { ++ printk(KERN_ERR DRIVER_NAME ": iic:%d: Unknown operation '%c ...' -- skipped\n", lnum, operation); ++ continue; ++ } ++ ++ /* Extract data values */ ++ size = 0; ++ while ( (*line != '\0') && (size < sizeof(data)) ) { ++ line = str_skip_blanks(line); ++ if ( *line != '\0' ) ++ sscanf(line, "%hhx", &data[size++]); ++ line = str_skip_chars(line); ++ } ++ ++ { ++ int i; ++ ++ printk(KERN_DEBUG DRIVER_NAME ": iic:%d: %c", lnum, operation); ++ for (i = 0; i < size; i++) ++ printk(" %02X", data[i]); ++ printk("\n"); ++ } ++ ++ /* Do nothing if there are less than 2 data bytes (address, command) */ ++ if ( size < 3 ) { ++ printk(KERN_ERR DRIVER_NAME ": iic:%d: Too few data for operation '%c ...' -- skipped\n", lnum, operation); ++ continue; ++ } ++ ++ /* First data byte is the i2c device address: ++ Warn if it does not match the standard i2c address */ ++ if ( data[0] != CAPSENSE_I2C_ADDR ) { ++ printk(KERN_WARNING DRIVER_NAME ": iic:%d: WARNING - Specified i2c address (%02X) differs from standard i2c address (%02X)\n", lnum, data[0], CAPSENSE_I2C_ADDR); ++ } ++ ++ /* Second data byte is the capsense register: ++ Warn if changing the device I2C address */ ++ if ( data[1] == CAP_I2C_ADDR_DM ) { ++ printk(KERN_WARNING DRIVER_NAME ": iic:%d: WARNING - Changing i2c address to %02X (I2C_ADDR_DM=%02X)\n", lnum, data[2] & 0x7F, data[2]); ++ } ++ ++ /* Send command to i2c device */ ++ mutex_lock