level up target xburst to linux kernel version 3.2.1

Thanks to the qi-hardware project - especially to Lars, Maarten and Xiangfu

SVN-Revision: 31218
v19.07.3_mercusys_ac12_duma
Mirko Vogt 12 years ago
parent 626e171764
commit b0971e69f2

@ -12,7 +12,7 @@ BOARDNAME:=Ingenic XBurst
FEATURES:=jffs2 targz ubifs audio
SUBTARGETS:=qi_lb60 n516 n526 id800wt
LINUX_VERSION:=2.6.37.6
LINUX_VERSION:=3.2.1
DEVICE_TYPE=other

@ -0,0 +1,318 @@
# CONFIG_ARCH_DMA_ADDR_T_64BIT is not set
# CONFIG_ARCH_HAS_ILOG2_U32 is not set
# CONFIG_ARCH_HAS_ILOG2_U64 is not set
CONFIG_ARCH_HIBERNATION_POSSIBLE=y
# CONFIG_ARCH_PHYS_ADDR_T_64BIT is not set
CONFIG_ARCH_POPULATES_NODE_MAP=y
CONFIG_ARCH_REQUIRE_GPIOLIB=y
# CONFIG_ARCH_SUPPORTS_MSI is not set
CONFIG_ARCH_SUSPEND_POSSIBLE=y
# CONFIG_ARPD is not set
# CONFIG_ATH79 is not set
# CONFIG_ATMEL_PWM is not set
# CONFIG_BATTERY_BQ20Z75 is not set
CONFIG_BATTERY_JZ4740=y
CONFIG_BCMA_POSSIBLE=y
# CONFIG_BLK_DEV_INITRD is not set
# CONFIG_BRIDGE is not set
CONFIG_CHARGER_GPIO=y
CONFIG_CONSOLE_TRANSLATIONS=y
CONFIG_CPU_HAS_PREFETCH=y
CONFIG_CPU_HAS_SYNC=y
CONFIG_CPU_LITTLE_ENDIAN=y
CONFIG_CPU_MIPS32=y
CONFIG_CPU_MIPS32_R1=y
CONFIG_CPU_MIPSR1=y
CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y
CONFIG_CPU_SUPPORTS_CPUFREQ=y
CONFIG_CPU_SUPPORTS_HIGHMEM=y
CONFIG_CRC16=y
CONFIG_CRYPTO_ALGAPI=y
CONFIG_CRYPTO_ALGAPI2=y
CONFIG_CRYPTO_DEFLATE=y
CONFIG_CRYPTO_LZO=y
CONFIG_CRYPTO_XZ=y
CONFIG_DEVMEM=y
CONFIG_DMA_NONCOHERENT=y
CONFIG_DUMMY_CONSOLE=y
CONFIG_EARLY_PRINTK=y
CONFIG_ELF_CORE=y
CONFIG_ENABLE_MUST_CHECK=y
CONFIG_EXT4_FS=y
CONFIG_FAT_FS=y
CONFIG_FB=y
CONFIG_FB_JZ4740=y
CONFIG_FB_SYS_COPYAREA=y
CONFIG_FB_SYS_FILLRECT=y
CONFIG_FB_SYS_IMAGEBLIT=y
# CONFIG_FB_TMIO is not set
# CONFIG_FB_WMT_GE_ROPS is not set
# CONFIG_FIRMWARE_EDID is not set
CONFIG_FONTS=y
# CONFIG_FONT_10x18 is not set
CONFIG_FONT_6x11=y
# CONFIG_FONT_7x14 is not set
# CONFIG_FONT_8x16 is not set
# CONFIG_FONT_8x8 is not set
# CONFIG_FONT_ACORN_8x8 is not set
# CONFIG_FONT_MINI_4x6 is not set
# CONFIG_FONT_PEARL_8x8 is not set
# CONFIG_FONT_SUN12x22 is not set
# CONFIG_FONT_SUN8x16 is not set
CONFIG_FORCE_MAX_ZONEORDER=12
CONFIG_FRAMEBUFFER_CONSOLE=y
# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set
# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set
CONFIG_FREEZER=y
CONFIG_FS_MBCACHE=y
CONFIG_GENERIC_ATOMIC64=y
CONFIG_GENERIC_CLOCKEVENTS=y
CONFIG_GENERIC_CLOCKEVENTS_BUILD=y
CONFIG_GENERIC_CMOS_UPDATE=y
CONFIG_GENERIC_GPIO=y
CONFIG_GENERIC_IRQ_CHIP=y
CONFIG_GENERIC_IRQ_SHOW=y
CONFIG_GPIOLIB=y
# CONFIG_HAMRADIO is not set
CONFIG_HARDWARE_WATCHPOINTS=y
CONFIG_HAS_DMA=y
CONFIG_HAS_IOMEM=y
CONFIG_HAS_IOPORT=y
CONFIG_HAVE_ARCH_JUMP_LABEL=y
CONFIG_HAVE_ARCH_KGDB=y
CONFIG_HAVE_CLK=y
CONFIG_HAVE_C_RECORDMCOUNT=y
CONFIG_HAVE_DMA_API_DEBUG=y
CONFIG_HAVE_DMA_ATTRS=y
CONFIG_HAVE_DYNAMIC_FTRACE=y
CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y
CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y
CONFIG_HAVE_FUNCTION_TRACER=y
CONFIG_HAVE_FUNCTION_TRACE_MCOUNT_TEST=y
CONFIG_HAVE_GENERIC_DMA_COHERENT=y
CONFIG_HAVE_GENERIC_HARDIRQS=y
CONFIG_HAVE_IDE=y
CONFIG_HAVE_IRQ_WORK=y
CONFIG_HAVE_OPROFILE=y
CONFIG_HAVE_PERF_EVENTS=y
CONFIG_HAVE_PWM=y
# CONFIG_HIGH_RES_TIMERS is not set
CONFIG_HW_CONSOLE=y
CONFIG_I2C=y
CONFIG_I2C_BOARDINFO=y
# CONFIG_I2C_PXA_PCI is not set
# CONFIG_INLINE_READ_UNLOCK is not set
# CONFIG_INLINE_READ_UNLOCK_IRQ is not set
# CONFIG_INLINE_SPIN_UNLOCK is not set
# CONFIG_INLINE_SPIN_UNLOCK_IRQ is not set
# CONFIG_INLINE_WRITE_UNLOCK is not set
# CONFIG_INLINE_WRITE_UNLOCK_IRQ is not set
CONFIG_INPUT=y
CONFIG_INPUT_EVDEV=y
# CONFIG_INPUT_GPIO_BUTTONS is not set
CONFIG_INPUT_KEYBOARD=y
CONFIG_INPUT_MOUSE=y
CONFIG_INPUT_MOUSEDEV=y
# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
# CONFIG_INPUT_PWM_BEEPER is not set
CONFIG_INPUT_UINPUT=y
CONFIG_IRQ_CPU=y
CONFIG_IRQ_FORCED_THREADING=y
CONFIG_JBD2=y
CONFIG_JZ4740_QI_LB60=y
CONFIG_KALLSYMS=y
CONFIG_KEXEC=y
# CONFIG_KEYBOARD_GPIO is not set
# CONFIG_LANTIQ is not set
CONFIG_LEGACY_PTYS=y
CONFIG_LEGACY_PTY_COUNT=2
CONFIG_LZO_COMPRESS=y
CONFIG_LZO_DECOMPRESS=y
CONFIG_MACH_JZ4740=y
CONFIG_MFD_CORE=y
CONFIG_MFD_JZ4740_ADC=y
# CONFIG_MINIX_FS_NATIVE_ENDIAN is not set
CONFIG_MIPS=y
CONFIG_MIPS_CPUFREQ=y
CONFIG_MIPS_FPU_EMU=y
CONFIG_MIPS_L1_CACHE_SHIFT=5
# CONFIG_MIPS_MACHINE is not set
CONFIG_MIPS_MT_DISABLED=y
CONFIG_MMC=y
CONFIG_MMC_BLOCK=y
# CONFIG_MMC_BLOCK_BOUNCE is not set
CONFIG_MMC_JZ4740=y
CONFIG_MMC_UNSAFE_RESUME=y
# CONFIG_MOUSE_BCM5974 is not set
CONFIG_MOUSE_PS2=y
# CONFIG_MOUSE_PS2_ALPS is not set
# CONFIG_MOUSE_PS2_ELANTECH is not set
# CONFIG_MOUSE_PS2_LOGIPS2PP is not set
# CONFIG_MOUSE_PS2_SYNAPTICS is not set
# CONFIG_MOUSE_PS2_TOUCHKIT is not set
# CONFIG_MOUSE_PS2_TRACKPOINT is not set
# CONFIG_MOUSE_SERIAL is not set
# CONFIG_MOUSE_VSXXXAA is not set
# CONFIG_MTD_CFI is not set
# CONFIG_MTD_COMPLEX_MAPPINGS is not set
CONFIG_MTD_NAND=y
CONFIG_MTD_NAND_ECC=y
CONFIG_MTD_NAND_JZ4740=y
# CONFIG_MTD_SM_COMMON is not set
CONFIG_MTD_UBI=y
CONFIG_MTD_UBI_BEB_RESERVE=1
# CONFIG_MTD_UBI_DEBUG is not set
# CONFIG_MTD_UBI_GLUEBI is not set
CONFIG_MTD_UBI_WL_THRESHOLD=4096
CONFIG_NEED_DMA_MAP_STATE=y
CONFIG_NEED_PER_CPU_KM=y
# CONFIG_NETWORK_FILESYSTEMS is not set
# CONFIG_NET_SCHED is not set
# CONFIG_NEW_LEDS is not set
CONFIG_NLS=y
CONFIG_NLS_ASCII=y
CONFIG_NLS_CODEPAGE_1250=y
CONFIG_NLS_CODEPAGE_1251=y
CONFIG_NLS_CODEPAGE_437=y
CONFIG_NLS_CODEPAGE_737=y
CONFIG_NLS_CODEPAGE_775=y
CONFIG_NLS_CODEPAGE_850=y
CONFIG_NLS_CODEPAGE_852=y
CONFIG_NLS_CODEPAGE_855=y
CONFIG_NLS_CODEPAGE_857=y
CONFIG_NLS_CODEPAGE_860=y
CONFIG_NLS_CODEPAGE_861=y
CONFIG_NLS_CODEPAGE_862=y
CONFIG_NLS_CODEPAGE_863=y
CONFIG_NLS_CODEPAGE_864=y
CONFIG_NLS_CODEPAGE_865=y
CONFIG_NLS_CODEPAGE_866=y
CONFIG_NLS_CODEPAGE_869=y
CONFIG_NLS_CODEPAGE_874=y
CONFIG_NLS_CODEPAGE_932=y
CONFIG_NLS_CODEPAGE_936=y
CONFIG_NLS_CODEPAGE_949=y
CONFIG_NLS_CODEPAGE_950=y
CONFIG_NLS_ISO8859_1=y
CONFIG_NLS_ISO8859_13=y
CONFIG_NLS_ISO8859_14=y
CONFIG_NLS_ISO8859_15=y
CONFIG_NLS_ISO8859_2=y
CONFIG_NLS_ISO8859_3=y
CONFIG_NLS_ISO8859_4=y
CONFIG_NLS_ISO8859_5=y
CONFIG_NLS_ISO8859_6=y
CONFIG_NLS_ISO8859_7=y
CONFIG_NLS_ISO8859_8=y
CONFIG_NLS_ISO8859_9=y
CONFIG_NLS_KOI8_R=y
CONFIG_NLS_KOI8_U=y
CONFIG_NLS_UTF8=y
CONFIG_PAGEFLAGS_EXTENDED=y
# CONFIG_PDA_POWER is not set
CONFIG_PERF_USE_VMALLOC=y
CONFIG_PM=y
CONFIG_PM_CLK=y
# CONFIG_PM_DEBUG is not set
CONFIG_PM_SLEEP=y
CONFIG_POWER_SUPPLY=y
# CONFIG_POWER_SUPPLY_DEBUG is not set
CONFIG_PREEMPT=y
CONFIG_PREEMPT_COUNT=y
# CONFIG_PREEMPT_NONE is not set
CONFIG_PREEMPT_RCU=y
CONFIG_PRINTK_TIME=y
CONFIG_PROC_PAGE_MONITOR=y
# CONFIG_QUOTACTL is not set
# CONFIG_RCU_BOOST is not set
CONFIG_REGMAP=y
CONFIG_REGMAP_I2C=y
CONFIG_RTC_CLASS=y
# CONFIG_RTC_DRV_CMOS is not set
CONFIG_RTC_DRV_JZ4740=y
# CONFIG_SCSI_DMA is not set
# CONFIG_SDIO_UART is not set
CONFIG_SERIO=y
# CONFIG_SERIO_I8042 is not set
CONFIG_SERIO_LIBPS2=y
# CONFIG_SERIO_RAW is not set
# CONFIG_SERIO_SERPORT is not set
CONFIG_SND=y
# CONFIG_SND_EMU10K1_SEQ is not set
CONFIG_SND_JACK=y
CONFIG_SND_JZ4740_SOC=y
CONFIG_SND_JZ4740_SOC_I2S=y
CONFIG_SND_JZ4740_SOC_QI_LB60=y
# CONFIG_SND_OPL3_LIB_SEQ is not set
# CONFIG_SND_OPL4_LIB_SEQ is not set
CONFIG_SND_PCM=y
# CONFIG_SND_RAWMIDI_SEQ is not set
# CONFIG_SND_SBAWE_SEQ is not set
CONFIG_SND_SOC=y
# CONFIG_SND_SOC_ALL_CODECS is not set
CONFIG_SND_SOC_I2C_AND_SPI=y
CONFIG_SND_SOC_JZ4740_CODEC=y
CONFIG_SND_TIMER=y
CONFIG_SOUND=y
# CONFIG_SOUND_OSS_CORE is not set
# CONFIG_SQUASHFS is not set
# CONFIG_STAGING is not set
CONFIG_SUSPEND=y
CONFIG_SUSPEND_FREEZER=y
# CONFIG_SYN_COOKIES is not set
CONFIG_SYS_HAS_CPU_MIPS32_R1=y
CONFIG_SYS_HAS_EARLY_PRINTK=y
CONFIG_SYS_SUPPORTS_32BIT_KERNEL=y
CONFIG_SYS_SUPPORTS_ARBIT_HZ=y
CONFIG_SYS_SUPPORTS_LITTLE_ENDIAN=y
CONFIG_TINY_PREEMPT_RCU=y
CONFIG_UBIFS_FS=y
CONFIG_UBIFS_FS_ADVANCED_COMPR=y
# CONFIG_UBIFS_FS_DEBUG is not set
CONFIG_UBIFS_FS_LZO=y
# CONFIG_UBIFS_FS_XATTR is not set
CONFIG_UBIFS_FS_XZ=y
CONFIG_UBIFS_FS_ZLIB=y
# CONFIG_USB_ARCH_HAS_EHCI is not set
# CONFIG_USB_ARCH_HAS_XHCI is not set
# CONFIG_USB_AUDIO is not set
# CONFIG_USB_CDC_COMPOSITE is not set
CONFIG_USB_COMMON=y
CONFIG_USB_ETH=y
# CONFIG_USB_ETH_EEM is not set
# CONFIG_USB_ETH_RNDIS is not set
# CONFIG_USB_FILE_STORAGE is not set
# CONFIG_USB_FUNCTIONFS is not set
CONFIG_USB_FUSB300=y
CONFIG_USB_GADGET=y
# CONFIG_USB_GADGETFS is not set
# CONFIG_USB_GADGET_DEBUG_FILES is not set
# CONFIG_USB_GADGET_DEBUG_FS is not set
CONFIG_USB_GADGET_DUALSPEED=y
CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
CONFIG_USB_GADGET_VBUS_DRAW=2
# CONFIG_USB_G_ACM_MS is not set
# CONFIG_USB_G_DBGP is not set
# CONFIG_USB_G_HID is not set
# CONFIG_USB_G_NCM is not set
# CONFIG_USB_G_PRINTER is not set
# CONFIG_USB_G_SERIAL is not set
# CONFIG_USB_JZ4740 is not set
# CONFIG_USB_M66592 is not set
# CONFIG_USB_MIDI_GADGET is not set
# CONFIG_USB_NET2272 is not set
# CONFIG_USB_R8A66597 is not set
CONFIG_USB_SUPPORT=y
# CONFIG_USB_ZERO is not set
CONFIG_VFAT_FS=y
# CONFIG_VGA_CONSOLE is not set
# CONFIG_VLAN_8021Q is not set
CONFIG_VT=y
CONFIG_VT_CONSOLE=y
CONFIG_VT_CONSOLE_SLEEP=y
CONFIG_VT_HW_CONSOLE_BINDING=y
# CONFIG_WATCHDOG is not set
CONFIG_XZ_DEC=y
CONFIG_ZONE_DMA_FLAG=0

@ -0,0 +1,26 @@
From 73aab29ddc44a3e1a7156473ad5bc9cd80704274 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Tue, 15 Mar 2011 12:49:15 +0100
Subject: [PATCH 01/21] ubi: Read only the vid header instead of the whole
page
---
drivers/mtd/ubi/io.c | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c
index f20b6f2..e66079d 100644
--- a/drivers/mtd/ubi/io.c
+++ b/drivers/mtd/ubi/io.c
@@ -1031,7 +1031,7 @@ int ubi_io_read_vid_hdr(struct ubi_device *ubi, int pnum,
p = (char *)vid_hdr - ubi->vid_hdr_shift;
read_err = ubi_io_read(ubi, p, pnum, ubi->vid_hdr_aloffset,
- ubi->vid_hdr_alsize);
+ UBI_VID_HDR_SIZE + ubi->vid_hdr_shift);
if (read_err && read_err != UBI_IO_BITFLIPS && !mtd_is_eccerr(read_err))
return read_err;
--
1.7.5.4

@ -0,0 +1,45 @@
From b971c50dc50c07f981a1619f977e7509db9c4eac Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Sat, 26 Feb 2011 15:30:07 +0100
Subject: [PATCH 03/21] NAND: Optimize NAND_ECC_HW_OOB_FIRST read
Avoid sending unnecessary READ commands to the chip.
---
drivers/mtd/nand/nand_base.c | 14 ++++++++++----
1 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 3ed9c5e..184aa93 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1294,9 +1294,15 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
uint8_t *ecc_calc = chip->buffers->ecccalc;
/* Read the OOB area first */
- chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
- chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+ if (mtd->writesize > 512) {
+ chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
+ }
for (i = 0; i < chip->ecc.total; i++)
ecc_code[i] = chip->oob_poi[eccpos[i]];
@@ -1465,7 +1471,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
if (realpage != chip->pagebuf || oob) {
bufpoi = aligned ? buf : chip->buffers->databuf;
- if (likely(sndcmd)) {
+ if (likely(sndcmd) && chip->ecc.mode != NAND_ECC_HW_OOB_FIRST) {
chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page);
sndcmd = 0;
}
--
1.7.5.4

@ -0,0 +1,151 @@
From 297d8a7bdfb755778d4189ca2861dd2a6125e972 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Tue, 15 Mar 2011 12:33:41 +0100
Subject: [PATCH 04/21] NAND: Add support for subpage reads for
NAND_ECC_HW_OOB_FIRST
---
drivers/mtd/nand/nand_base.c | 78 ++++++++++++++++++++++++++++++++++++++++--
include/linux/mtd/nand.h | 8 ++--
2 files changed, 79 insertions(+), 7 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 184aa93..0131e84 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1143,7 +1143,7 @@ static int nand_read_page_swecc(struct mtd_info *mtd, struct nand_chip *chip,
* @bufpoi: buffer to store read data
*/
static int nand_read_subpage(struct mtd_info *mtd, struct nand_chip *chip,
- uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi)
+ uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, int page)
{
int start_step, end_step, num_steps;
uint32_t *eccpos = chip->ecc.layout->eccpos;
@@ -1324,6 +1324,75 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
}
/**
+ * nand_read_subpage_hwecc_oob_first - [REPLACABLE] hw ecc based sub-page read function
+ * @mtd: mtd info structure
+ * @chip: nand chip info structure
+ * @data_offs: offset of requested data within the page
+ * @readlen: data length
+ * @bufpoi: buffer to store read data
+ * @page: page number to read
+ *
+ * Hardware ECC for large page chips, require OOB to be read first.
+ * For this ECC mode, the write_page method is re-used from ECC_HW.
+ * These methods read/write ECC from the OOB area, unlike the
+ * ECC_HW_SYNDROME support with multiple ECC steps, follows the
+ * "infix ECC" scheme and reads/writes ECC from the data area, by
+ * overwriting the NAND manufacturer bad block markings.
+ */
+static int nand_read_subpage_hwecc_oob_first(struct mtd_info *mtd, struct nand_chip *chip,
+ uint32_t data_offs, uint32_t readlen, uint8_t *bufpoi, int page)
+{
+ int start_step, end_step, num_steps;
+ uint32_t *eccpos = chip->ecc.layout->eccpos;
+ uint8_t *p;
+ int data_col_addr;
+ int eccsize = chip->ecc.size;
+ int eccbytes = chip->ecc.bytes;
+ uint8_t *ecc_code = chip->buffers->ecccode;
+ uint8_t *ecc_calc = chip->buffers->ecccalc;
+ int i;
+
+ /* Column address wihin the page aligned to ECC size */
+ start_step = data_offs / chip->ecc.size;
+ end_step = (data_offs + readlen - 1) / chip->ecc.size;
+ num_steps = end_step - start_step + 1;
+
+ data_col_addr = start_step * chip->ecc.size;
+
+ /* Read the OOB area first */
+ if (mtd->writesize > 512) {
+ chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
+ } else {
+ chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
+ chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, data_col_addr, page);
+ }
+
+ for (i = 0; i < chip->ecc.total; i++)
+ ecc_code[i] = chip->oob_poi[eccpos[i]];
+
+ p = bufpoi + data_col_addr;
+
+ for (i = eccbytes * start_step; num_steps; num_steps--, i += eccbytes, p += eccsize) {
+ int stat;
+
+ chip->ecc.hwctl(mtd, NAND_ECC_READ);
+ chip->read_buf(mtd, p, eccsize);
+ chip->ecc.calculate(mtd, p, &ecc_calc[i]);
+
+ stat = chip->ecc.correct(mtd, p, &ecc_code[i], NULL);
+ if (stat < 0)
+ mtd->ecc_stats.failed++;
+ else
+ mtd->ecc_stats.corrected += stat;
+ }
+
+ return 0;
+}
+
+/**
* nand_read_page_syndrome - [REPLACEABLE] hardware ECC syndrome based page read
* @mtd: mtd info structure
* @chip: nand chip info structure
@@ -1482,7 +1551,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from,
bufpoi, page);
else if (!aligned && NAND_SUBPAGE_READ(chip) && !oob)
ret = chip->ecc.read_subpage(mtd, chip,
- col, bytes, bufpoi);
+ col, bytes, bufpoi, page);
else
ret = chip->ecc.read_page(mtd, chip, bufpoi,
page);
@@ -3292,8 +3361,11 @@ int nand_scan_tail(struct mtd_info *mtd)
"hardware ECC not possible\n");
BUG();
}
- if (!chip->ecc.read_page)
+ if (!chip->ecc.read_page) {
chip->ecc.read_page = nand_read_page_hwecc_oob_first;
+ if (!chip->ecc.read_subpage)
+ chip->ecc.read_subpage = nand_read_subpage_hwecc_oob_first;
+ }
case NAND_ECC_HW:
/* Use standard hwecc read page function? */
diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h
index 904131b..9034100 100644
--- a/include/linux/mtd/nand.h
+++ b/include/linux/mtd/nand.h
@@ -211,9 +211,9 @@ typedef enum {
#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
-/* Large page NAND with SOFT_ECC should support subpage reads */
-#define NAND_SUBPAGE_READ(chip) ((chip->ecc.mode == NAND_ECC_SOFT) \
- && (chip->page_shift > 9))
+/* Large page NAND with read_subpage set should support subpage reads */
+#define NAND_SUBPAGE_READ(chip) (((chip)->ecc.read_subpage) \
+ && ((chip)->page_shift > 9))
/* Mask to zero out the chip options, which come from the id table */
#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR)
@@ -367,7 +367,7 @@ struct nand_ecc_ctrl {
int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip,
uint8_t *buf, int page);
int (*read_subpage)(struct mtd_info *mtd, struct nand_chip *chip,
- uint32_t offs, uint32_t len, uint8_t *buf);
+ uint32_t offs, uint32_t len, uint8_t *buf, int page);
void (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
const uint8_t *buf);
int (*write_oob_raw)(struct mtd_info *mtd, struct nand_chip *chip,
--
1.7.5.4

@ -0,0 +1,58 @@
From 4f28237a750afd1112b6f1266d36f8b718efe89c Mon Sep 17 00:00:00 2001
From: Xiangfu Liu <xiangfu@sharism.cc>
Date: Tue, 6 Mar 2012 11:19:26 +0800
Subject: [PATCH 05/21] NAND-Optimize-reading-the-eec-data-for-the-JZ4740
---
drivers/mtd/nand/nand_base.c | 14 ++++----------
1 files changed, 4 insertions(+), 10 deletions(-)
diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c
index 0131e84..74b8cce 100644
--- a/drivers/mtd/nand/nand_base.c
+++ b/drivers/mtd/nand/nand_base.c
@@ -1295,8 +1295,8 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
/* Read the OOB area first */
if (mtd->writesize > 512) {
- chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize + eccpos[0], page);
+ chip->read_buf(mtd, ecc_code, chip->ecc.total);
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, 0, -1);
} else {
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
@@ -1304,9 +1304,6 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd,
chip->cmdfunc(mtd, NAND_CMD_READ0, 0, page);
}
- for (i = 0; i < chip->ecc.total; i++)
- ecc_code[i] = chip->oob_poi[eccpos[i]];
-
for (i = 0; eccsteps; eccsteps--, i += eccbytes, p += eccsize) {
int stat;
@@ -1361,8 +1358,8 @@ static int nand_read_subpage_hwecc_oob_first(struct mtd_info *mtd, struct nand_c
/* Read the OOB area first */
if (mtd->writesize > 512) {
- chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize, page);
- chip->read_buf(mtd, chip->oob_poi, mtd->oobsize);
+ chip->cmdfunc(mtd, NAND_CMD_READ0, mtd->writesize + eccpos[0], page);
+ chip->read_buf(mtd, ecc_code, chip->ecc.total);
chip->cmdfunc(mtd, NAND_CMD_RNDOUT, data_col_addr, -1);
} else {
chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page);
@@ -1370,9 +1367,6 @@ static int nand_read_subpage_hwecc_oob_first(struct mtd_info *mtd, struct nand_c
chip->cmdfunc(mtd, NAND_CMD_READ0, data_col_addr, page);
}
- for (i = 0; i < chip->ecc.total; i++)
- ecc_code[i] = chip->oob_poi[eccpos[i]];
-
p = bufpoi + data_col_addr;
for (i = eccbytes * start_step; num_steps; num_steps--, i += eccbytes, p += eccsize) {
--
1.7.5.4

@ -0,0 +1,426 @@
From d5814bdb661d3dac61422f8f69e459be884c9a9d Mon Sep 17 00:00:00 2001
From: Maarten ter Huurne <maarten@treewalker.org>
Date: Tue, 2 Aug 2011 10:49:28 +0200
Subject: [PATCH 06/21] MTD: NAND: JZ4740: Multi-bank support with
autodetection
The platform data can now specify which external memory banks to probe
for NAND chips, and in which order. Banks that contain a NAND are used
and the other banks are freed.
Squashed version of development done in jz-2.6.38 branch.
Original patch by Lars-Peter Clausen with some bug fixes from me.
Thanks to Paul Cercueil for the initial autodetection patch.
---
arch/mips/include/asm/mach-jz4740/jz4740_nand.h | 4 +
arch/mips/jz4740/platform.c | 20 ++-
drivers/mtd/nand/jz4740_nand.c | 228 +++++++++++++++++++----
3 files changed, 215 insertions(+), 37 deletions(-)
diff --git a/arch/mips/include/asm/mach-jz4740/jz4740_nand.h b/arch/mips/include/asm/mach-jz4740/jz4740_nand.h
index bb5b9a4..986982d 100644
--- a/arch/mips/include/asm/mach-jz4740/jz4740_nand.h
+++ b/arch/mips/include/asm/mach-jz4740/jz4740_nand.h
@@ -19,6 +19,8 @@
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
+#define JZ_NAND_NUM_BANKS 4
+
struct jz_nand_platform_data {
int num_partitions;
struct mtd_partition *partitions;
@@ -27,6 +29,8 @@ struct jz_nand_platform_data {
unsigned int busy_gpio;
+ unsigned char banks[JZ_NAND_NUM_BANKS];
+
void (*ident_callback)(struct platform_device *, struct nand_chip *,
struct mtd_partition **, int *num_partitions);
};
diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c
index 10929e2..e342ed4 100644
--- a/arch/mips/jz4740/platform.c
+++ b/arch/mips/jz4740/platform.c
@@ -157,11 +157,29 @@ static struct resource jz4740_nand_resources[] = {
.flags = IORESOURCE_MEM,
},
{
- .name = "bank",
+ .name = "bank1",
.start = 0x18000000,
.end = 0x180C0000 - 1,
.flags = IORESOURCE_MEM,
},
+ {
+ .name = "bank2",
+ .start = 0x14000000,
+ .end = 0x140C0000 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "bank3",
+ .start = 0x0C000000,
+ .end = 0x0C0C0000 - 1,
+ .flags = IORESOURCE_MEM,
+ },
+ {
+ .name = "bank4",
+ .start = 0x08000000,
+ .end = 0x080C0000 - 1,
+ .flags = IORESOURCE_MEM,
+ },
};
struct platform_device jz4740_nand_device = {
diff --git a/drivers/mtd/nand/jz4740_nand.c b/drivers/mtd/nand/jz4740_nand.c
index e266407..b254b99 100644
--- a/drivers/mtd/nand/jz4740_nand.c
+++ b/drivers/mtd/nand/jz4740_nand.c
@@ -52,9 +52,10 @@
#define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT((x) << 1)
#define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT(((x) << 1) + 1)
+#define JZ_NAND_CTRL_ASSERT_CHIP_MASK 0xaa
-#define JZ_NAND_MEM_ADDR_OFFSET 0x10000
#define JZ_NAND_MEM_CMD_OFFSET 0x08000
+#define JZ_NAND_MEM_ADDR_OFFSET 0x10000
struct jz_nand {
struct mtd_info mtd;
@@ -62,8 +63,11 @@ struct jz_nand {
void __iomem *base;
struct resource *mem;
- void __iomem *bank_base;
- struct resource *bank_mem;
+ unsigned char banks[JZ_NAND_NUM_BANKS];
+ void __iomem *bank_base[JZ_NAND_NUM_BANKS];
+ struct resource *bank_mem[JZ_NAND_NUM_BANKS];
+
+ int selected_bank;
struct jz_nand_platform_data *pdata;
bool is_reading;
@@ -74,26 +78,50 @@ static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd)
return container_of(mtd, struct jz_nand, mtd);
}
+static void jz_nand_select_chip(struct mtd_info *mtd, int chipnr)
+{
+ struct jz_nand *nand = mtd_to_jz_nand(mtd);
+ struct nand_chip *chip = mtd->priv;
+ uint32_t ctrl;
+ int banknr;
+
+ ctrl = readl(nand->base + JZ_REG_NAND_CTRL);
+ ctrl &= ~JZ_NAND_CTRL_ASSERT_CHIP_MASK;
+
+ if (chipnr == -1) {
+ banknr = -1;
+ } else {
+ banknr = nand->banks[chipnr] - 1;
+ chip->IO_ADDR_R = nand->bank_base[banknr];
+ chip->IO_ADDR_W = nand->bank_base[banknr];
+ }
+ writel(ctrl, nand->base + JZ_REG_NAND_CTRL);
+
+ nand->selected_bank = banknr;
+}
+
static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl)
{
struct jz_nand *nand = mtd_to_jz_nand(mtd);
struct nand_chip *chip = mtd->priv;
uint32_t reg;
+ void __iomem *bank_base = nand->bank_base[nand->selected_bank];
+
+ BUG_ON(nand->selected_bank < 0);
if (ctrl & NAND_CTRL_CHANGE) {
BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE));
if (ctrl & NAND_ALE)
- chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_ADDR_OFFSET;
+ bank_base += JZ_NAND_MEM_ADDR_OFFSET;
else if (ctrl & NAND_CLE)
- chip->IO_ADDR_W = nand->bank_base + JZ_NAND_MEM_CMD_OFFSET;
- else
- chip->IO_ADDR_W = nand->bank_base;
+ bank_base += JZ_NAND_MEM_CMD_OFFSET;
+ chip->IO_ADDR_W = bank_base;
reg = readl(nand->base + JZ_REG_NAND_CTRL);
if (ctrl & NAND_NCE)
- reg |= JZ_NAND_CTRL_ASSERT_CHIP(0);
+ reg |= JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank);
else
- reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0);
+ reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(nand->selected_bank);
writel(reg, nand->base + JZ_REG_NAND_CTRL);
}
if (dat != NAND_CMD_NONE)
@@ -252,7 +280,7 @@ static int jz_nand_correct_ecc_rs(struct mtd_info *mtd, uint8_t *dat,
}
static int jz_nand_ioremap_resource(struct platform_device *pdev,
- const char *name, struct resource **res, void __iomem **base)
+ const char *name, struct resource **res, void *__iomem *base)
{
int ret;
@@ -288,6 +316,90 @@ err:
return ret;
}
+static inline void jz_nand_iounmap_resource(struct resource *res, void __iomem *base)
+{
+ iounmap(base);
+ release_mem_region(res->start, resource_size(res));
+}
+
+static int __devinit jz_nand_detect_bank(struct platform_device *pdev, struct jz_nand *nand, unsigned char bank, size_t chipnr, uint8_t *nand_maf_id, uint8_t *nand_dev_id) {
+ int ret;
+ int gpio;
+ char gpio_name[9];
+ char res_name[6];
+ uint32_t ctrl;
+ struct mtd_info *mtd = &nand->mtd;
+ struct nand_chip *chip = &nand->chip;
+
+ /* Request GPIO port. */
+ gpio = JZ_GPIO_MEM_CS0 + bank - 1;
+ sprintf(gpio_name, "NAND CS%d", bank);
+ ret = gpio_request(gpio, gpio_name);
+ if (ret) {
+ dev_warn(&pdev->dev,
+ "Failed to request %s gpio %d: %d\n",
+ gpio_name, gpio, ret);
+ goto notfound_gpio;
+ }
+
+ /* Request I/O resource. */
+ sprintf(res_name, "bank%d", bank);
+ ret = jz_nand_ioremap_resource(pdev, res_name,
+ &nand->bank_mem[bank - 1],
+ &nand->bank_base[bank - 1]);
+ if (ret)
+ goto notfound_resource;
+
+ /* Enable chip in bank. */
+ jz_gpio_set_function(gpio, JZ_GPIO_FUNC_MEM_CS0);
+ ctrl = readl(nand->base + JZ_REG_NAND_CTRL);
+ ctrl |= JZ_NAND_CTRL_ENABLE_CHIP(bank - 1);
+ writel(ctrl, nand->base + JZ_REG_NAND_CTRL);
+
+ if (chipnr == 0) {
+ /* Detect first chip. */
+ ret = nand_scan_ident(mtd, 1, NULL);
+ if (ret)
+ goto notfound_id;
+
+ /* Retrieve the IDs from the first chip. */
+ chip->select_chip(mtd, 0);
+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+ *nand_maf_id = chip->read_byte(mtd);
+ *nand_dev_id = chip->read_byte(mtd);
+ } else {
+ /* Detect additional chip. */
+ chip->select_chip(mtd, chipnr);
+ chip->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+ chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1);
+ if (*nand_maf_id != chip->read_byte(mtd)
+ || *nand_dev_id != chip->read_byte(mtd)) {
+ ret = -ENODEV;
+ goto notfound_id;
+ }
+
+ /* Update size of the MTD. */
+ chip->numchips++;
+ mtd->size += chip->chipsize;
+ }
+
+ dev_info(&pdev->dev, "Found chip %i on bank %i\n", chipnr, bank);
+ return 0;
+
+notfound_id:
+ dev_info(&pdev->dev, "No chip found on bank %i\n", bank);
+ ctrl &= ~(JZ_NAND_CTRL_ENABLE_CHIP(bank - 1));
+ writel(ctrl, nand->base + JZ_REG_NAND_CTRL);
+ jz_gpio_set_function(gpio, JZ_GPIO_FUNC_NONE);
+ jz_nand_iounmap_resource(nand->bank_mem[bank - 1],
+ nand->bank_base[bank - 1]);
+notfound_resource:
+ gpio_free(gpio);
+notfound_gpio:
+ return ret;
+}
+
static int __devinit jz_nand_probe(struct platform_device *pdev)
{
int ret;
@@ -295,6 +407,8 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
struct nand_chip *chip;
struct mtd_info *mtd;
struct jz_nand_platform_data *pdata = pdev->dev.platform_data;
+ size_t chipnr, bank_idx;
+ uint8_t nand_maf_id = 0, nand_dev_id = 0;
nand = kzalloc(sizeof(*nand), GFP_KERNEL);
if (!nand) {
@@ -305,10 +419,6 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
ret = jz_nand_ioremap_resource(pdev, "mmio", &nand->mem, &nand->base);
if (ret)
goto err_free;
- ret = jz_nand_ioremap_resource(pdev, "bank", &nand->bank_mem,
- &nand->bank_base);
- if (ret)
- goto err_iounmap_mmio;
if (pdata && gpio_is_valid(pdata->busy_gpio)) {
ret = gpio_request(pdata->busy_gpio, "NAND busy pin");
@@ -316,7 +426,7 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
dev_err(&pdev->dev,
"Failed to request busy gpio %d: %d\n",
pdata->busy_gpio, ret);
- goto err_iounmap_mem;
+ goto err_iounmap_mmio;
}
}
@@ -338,22 +448,51 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
chip->chip_delay = 50;
chip->cmd_ctrl = jz_nand_cmd_ctrl;
+ chip->select_chip = jz_nand_select_chip;
if (pdata && gpio_is_valid(pdata->busy_gpio))
chip->dev_ready = jz_nand_dev_ready;
- chip->IO_ADDR_R = nand->bank_base;
- chip->IO_ADDR_W = nand->bank_base;
-
nand->pdata = pdata;
platform_set_drvdata(pdev, nand);
- writel(JZ_NAND_CTRL_ENABLE_CHIP(0), nand->base + JZ_REG_NAND_CTRL);
-
- ret = nand_scan_ident(mtd, 1, NULL);
- if (ret) {
- dev_err(&pdev->dev, "Failed to scan nand\n");
- goto err_gpio_free;
+ /* We are going to autodetect NAND chips in the banks specified in the
+ * platform data. Although nand_scan_ident() can detect multiple chips,
+ * it requires those chips to be numbered consecuitively, which is not
+ * always the case for external memory banks. And a fixed chip-to-bank
+ * mapping is not practical either, since for example Dingoo units
+ * produced at different times have NAND chips in different banks.
+ */
+ chipnr = 0;
+ for (bank_idx = 0; bank_idx < JZ_NAND_NUM_BANKS; bank_idx++) {
+ unsigned char bank;
+
+ /* If there is no platform data, look for NAND in bank 1,
+ * which is the most likely bank since it is the only one
+ * that can be booted from.
+ */
+ bank = pdata ? pdata->banks[bank_idx] : bank_idx ^ 1;
+ if (bank == 0)
+ break;
+ if (bank > JZ_NAND_NUM_BANKS) {
+ dev_warn(&pdev->dev,
+ "Skipping non-existing bank: %d\n", bank);
+ continue;
+ }
+ /* The detection routine will directly or indirectly call
+ * jz_nand_select_chip(), so nand->banks has to contain the
+ * bank we're checking.
+ */
+ nand->banks[chipnr] = bank;
+ if (jz_nand_detect_bank(pdev, nand, bank, chipnr,
+ &nand_maf_id, &nand_dev_id) == 0)
+ chipnr++;
+ else
+ nand->banks[chipnr] = 0;
+ }
+ if (chipnr == 0) {
+ dev_err(&pdev->dev, "No NAND chips found\n");
+ goto err_gpio_busy;
}
if (pdata && pdata->ident_callback) {
@@ -363,8 +502,8 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
ret = nand_scan_tail(mtd);
if (ret) {
- dev_err(&pdev->dev, "Failed to scan nand\n");
- goto err_gpio_free;
+ dev_err(&pdev->dev, "Failed to scan NAND\n");
+ goto err_unclaim_banks;
}
ret = mtd_device_parse_register(mtd, NULL, 0,
@@ -381,14 +520,21 @@ static int __devinit jz_nand_probe(struct platform_device *pdev)
return 0;
err_nand_release:
- nand_release(&nand->mtd);
-err_gpio_free:
+ nand_release(mtd);
+err_unclaim_banks:
+ while (chipnr--) {
+ unsigned char bank = nand->banks[chipnr];
+ gpio_free(JZ_GPIO_MEM_CS0 + bank - 1);
+ jz_nand_iounmap_resource(nand->bank_mem[bank - 1],
+ nand->bank_base[bank - 1]);
+ }
+ writel(0, nand->base + JZ_REG_NAND_CTRL);
+err_gpio_busy:
+ if (pdata && gpio_is_valid(pdata->busy_gpio))
+ gpio_free(pdata->busy_gpio);
platform_set_drvdata(pdev, NULL);
- gpio_free(pdata->busy_gpio);
-err_iounmap_mem:
- iounmap(nand->bank_base);
err_iounmap_mmio:
- iounmap(nand->base);
+ jz_nand_iounmap_resource(nand->mem, nand->base);
err_free:
kfree(nand);
return ret;
@@ -397,16 +543,26 @@ err_free:
static int __devexit jz_nand_remove(struct platform_device *pdev)
{
struct jz_nand *nand = platform_get_drvdata(pdev);
+ struct jz_nand_platform_data *pdata = pdev->dev.platform_data;
+ size_t i;
nand_release(&nand->mtd);
/* Deassert and disable all chips */
writel(0, nand->base + JZ_REG_NAND_CTRL);
- iounmap(nand->bank_base);
- release_mem_region(nand->bank_mem->start, resource_size(nand->bank_mem));
- iounmap(nand->base);
- release_mem_region(nand->mem->start, resource_size(nand->mem));
+ for (i = 0; i < JZ_NAND_NUM_BANKS; ++i) {
+ unsigned char bank = nand->banks[i];
+ if (bank != 0) {
+ jz_nand_iounmap_resource(nand->bank_mem[bank - 1],
+ nand->bank_base[bank - 1]);
+ gpio_free(JZ_GPIO_MEM_CS0 + bank - 1);
+ }
+ }
+ if (pdata && gpio_is_valid(pdata->busy_gpio))
+ gpio_free(pdata->busy_gpio);
+
+ jz_nand_iounmap_resource(nand->mem, nand->base);
platform_set_drvdata(pdev, NULL);
kfree(nand);
--
1.7.5.4

@ -0,0 +1,314 @@
From 2248fb9c47fe7e72c735d6c16aba27bf92e1673a Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Sun, 1 Aug 2010 21:19:40 +0200
Subject: [PATCH 07/21] Add ili8960 lcd driver
---
drivers/video/backlight/Kconfig | 7 +
drivers/video/backlight/Makefile | 1 +
drivers/video/backlight/ili8960.c | 263 +++++++++++++++++++++++++++++++++++++
3 files changed, 271 insertions(+), 0 deletions(-)
create mode 100644 drivers/video/backlight/ili8960.c
diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig
index 278aeaa..f617883 100644
--- a/drivers/video/backlight/Kconfig
+++ b/drivers/video/backlight/Kconfig
@@ -59,6 +59,13 @@ config LCD_LTV350QV
The LTV350QV panel is present on all ATSTK1000 boards.
+config LCD_ILI8960
+ tristate "Ilitek ili8960 LCD driver"
+ depends on LCD_CLASS_DEVICE && SPI
+ default n
+ help
+ Driver for the Ilitek ili8960 LCD controller chip.
+
config LCD_ILI9320
tristate
help
diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile
index fdd1fc4..01ec378 100644
--- a/drivers/video/backlight/Makefile
+++ b/drivers/video/backlight/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o
obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o
obj-$(CONFIG_LCD_LMS283GF05) += lms283gf05.o
obj-$(CONFIG_LCD_LTV350QV) += ltv350qv.o
+obj-$(CONFIG_LCD_ILI8960) += ili8960.o
obj-$(CONFIG_LCD_ILI9320) += ili9320.o
obj-$(CONFIG_LCD_PLATFORM) += platform_lcd.o
obj-$(CONFIG_LCD_VGG2432A4) += vgg2432a4.o
diff --git a/drivers/video/backlight/ili8960.c b/drivers/video/backlight/ili8960.c
new file mode 100644
index 0000000..1438e92
--- /dev/null
+++ b/drivers/video/backlight/ili8960.c
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2009-2010, Lars-Peter Clausen <lars@metafoo.de>
+ * Driver for Ilitek ili8960 LCD
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/spi/spi.h>
+#include <linux/lcd.h>
+#include <linux/backlight.h>
+#include <linux/delay.h>
+
+struct ili8960 {
+ struct spi_device *spi;
+ struct lcd_device *lcd;
+ struct backlight_device *bl;
+ bool enabled;
+ unsigned int brightness;
+};
+
+#define ILI8960_REG_BRIGHTNESS 0x03
+#define ILI8960_REG_POWER 0x05
+#define ILI8960_REG_CONTRAST 0x0d
+
+static int ili8960_write_reg(struct spi_device *spi, uint8_t reg,
+ uint8_t data)
+{
+ uint8_t buf[2];
+ buf[0] = ((reg & 0x40) << 1) | (reg & 0x3f);
+ buf[1] = data;
+
+ return spi_write(spi, buf, sizeof(buf));
+}
+
+static int ili8960_programm_power(struct spi_device *spi, bool enabled)
+{
+ int ret;
+
+ if (enabled)
+ mdelay(20);
+
+ ret = ili8960_write_reg(spi, ILI8960_REG_POWER, enabled ? 0xc7 : 0xc6);
+
+ if (!enabled)
+ mdelay(20);
+
+ return ret;
+}
+
+static int ili8960_set_power(struct lcd_device *lcd, int power)
+{
+ struct ili8960 *ili8960 = lcd_get_data(lcd);
+
+ switch (power) {
+ case FB_BLANK_UNBLANK:
+ ili8960->enabled = true;
+ break;
+ default:
+ ili8960->enabled = false;
+ break;
+ }
+
+ return ili8960_programm_power(ili8960->spi, ili8960->enabled);
+}
+
+static int ili8960_get_power(struct lcd_device *lcd)
+{
+ struct ili8960 *ili8960 = lcd_get_data(lcd);
+ return ili8960->enabled ? FB_BLANK_UNBLANK : FB_BLANK_POWERDOWN;
+}
+
+static int ili8960_set_contrast(struct lcd_device *lcd, int contrast)
+{
+ struct ili8960 *ili8960 = lcd_get_data(lcd);
+
+ return ili8960_write_reg(ili8960->spi, ILI8960_REG_CONTRAST, contrast);
+}
+
+static int ili8960_set_mode(struct lcd_device *lcd, struct fb_videomode *mode)
+{
+ if (mode->xres != 320 && mode->yres != 240)
+ return -EINVAL;
+
+ return 0;
+}
+
+static int ili8960_set_brightness(struct ili8960 *ili8960, int brightness)
+{
+ int ret;
+
+ ret = ili8960_write_reg(ili8960->spi, ILI8960_REG_BRIGHTNESS, brightness);
+
+ if (ret == 0)
+ ili8960->brightness = brightness;
+
+ return ret;
+}
+
+static ssize_t ili8960_show_brightness(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct lcd_device *ld = to_lcd_device(dev);
+ struct ili8960 *ili8960 = lcd_get_data(ld);
+
+ return sprintf(buf, "%u\n", ili8960->brightness);
+}
+
+static ssize_t ili8960_store_brightness(struct device *dev,
+ struct device_attribute *attr, const char *buf, size_t count)
+{
+ struct lcd_device *ld = to_lcd_device(dev);
+ struct ili8960 *ili8960 = lcd_get_data(ld);
+ unsigned long brightness;
+ int ret;
+
+ ret = strict_strtoul(buf, 0, &brightness);
+ if (ret)
+ return ret;
+
+ if (brightness > 255)
+ return -EINVAL;
+
+ ili8960_set_brightness(ili8960, brightness);
+
+ return count;
+}
+
+
+static DEVICE_ATTR(brightness, 0644, ili8960_show_brightness,
+ ili8960_store_brightness);
+
+static struct lcd_ops ili8960_lcd_ops = {
+ .set_power = ili8960_set_power,
+ .get_power = ili8960_get_power,
+ .set_contrast = ili8960_set_contrast,
+ .set_mode = ili8960_set_mode,
+};
+
+static int __devinit ili8960_probe(struct spi_device *spi)
+{
+ int ret;
+ struct ili8960 *ili8960;
+
+ ili8960 = kmalloc(sizeof(*ili8960), GFP_KERNEL);
+ if (!ili8960)
+ return -ENOMEM;
+
+ spi->bits_per_word = 8;
+ spi->mode = SPI_MODE_3;
+
+ ret = spi_setup(spi);
+ if (ret) {
+ dev_err(&spi->dev, "Failed to setup spi\n");
+ goto err_free_ili8960;
+ }
+
+ ili8960->spi = spi;
+
+ ili8960->lcd = lcd_device_register("ili8960-lcd", &spi->dev, ili8960,
+ &ili8960_lcd_ops);
+
+ if (IS_ERR(ili8960->lcd)) {
+ ret = PTR_ERR(ili8960->lcd);
+ dev_err(&spi->dev, "Failed to register lcd device: %d\n", ret);
+ goto err_free_ili8960;
+ }
+
+ ili8960->lcd->props.max_contrast = 255;
+
+ ret = device_create_file(&ili8960->lcd->dev, &dev_attr_brightness);
+ if (ret)
+ goto err_unregister_lcd;
+
+ ili8960_programm_power(ili8960->spi, true);
+ ili8960->enabled = true;
+
+ spi_set_drvdata(spi, ili8960);
+
+ ili8960_write_reg(spi, 0x13, 0x01);
+
+ return 0;
+err_unregister_lcd:
+ lcd_device_unregister(ili8960->lcd);
+err_free_ili8960:
+ kfree(ili8960);
+ return ret;
+}
+
+static int __devexit ili8960_remove(struct spi_device *spi)
+{
+ struct ili8960 *ili8960 = spi_get_drvdata(spi);
+
+ device_remove_file(&ili8960->lcd->dev, &dev_attr_brightness);
+ lcd_device_unregister(ili8960->lcd);
+
+ spi_set_drvdata(spi, NULL);
+ kfree(ili8960);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+
+static int ili8960_suspend(struct spi_device *spi, pm_message_t state)
+{
+ struct ili8960 *ili8960 = spi_get_drvdata(spi);
+
+ if (ili8960->enabled)
+ ili8960_programm_power(ili8960->spi, false);
+
+ return 0;
+}
+
+static int ili8960_resume(struct spi_device *spi)
+{
+ struct ili8960 *ili8960 = spi_get_drvdata(spi);
+
+ if (ili8960->enabled)
+ ili8960_programm_power(ili8960->spi, true);
+
+ return 0;
+}
+
+#else
+#define ili8960_suspend NULL
+#define ili8960_resume NULL
+#endif
+
+static struct spi_driver ili8960_driver = {
+ .driver = {
+ .name = "ili8960",
+ .owner = THIS_MODULE,
+ },
+ .probe = ili8960_probe,
+ .remove = __devexit_p(ili8960_remove),
+ .suspend = ili8960_suspend,
+ .resume = ili8960_resume,
+};
+
+static int __init ili8960_init(void)
+{
+ return spi_register_driver(&ili8960_driver);
+}
+module_init(ili8960_init);
+
+static void __exit ili8960_exit(void)
+{
+ spi_unregister_driver(&ili8960_driver);
+}
+module_exit(ili8960_exit)
+
+MODULE_AUTHOR("Lars-Peter Clausen");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("LCD driver for Ilitek ili8960");
+MODULE_ALIAS("spi:ili8960");
--
1.7.5.4

@ -0,0 +1,26 @@
From e5f25e25ed6c0478cbba7a32891b911e3183dad4 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Wed, 13 Oct 2010 01:17:24 +0200
Subject: [PATCH 08/21] qi_lb60: Don't use 3-wire spi mode for the display for
now
The spi_gpio driver does not support 3-wire mode.
---
arch/mips/jz4740/board-qi_lb60.c | 1 -
1 files changed, 0 insertions(+), 1 deletions(-)
diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c
index c3b04be..b622941 100644
--- a/arch/mips/jz4740/board-qi_lb60.c
+++ b/arch/mips/jz4740/board-qi_lb60.c
@@ -310,7 +310,6 @@ static struct spi_board_info qi_lb60_spi_board_info[] = {
.chip_select = 0,
.bus_num = 1,
.max_speed_hz = 30 * 1000,
- .mode = SPI_3WIRE,
},
};
--
1.7.5.4

@ -0,0 +1,133 @@
From 4003b9a2c05f4d0d37535c3dffbf4a7b47d5c36c Mon Sep 17 00:00:00 2001
From: Maarten ter Huurne <maarten@treewalker.org>
Date: Wed, 8 Sep 2010 02:31:19 +0200
Subject: [PATCH 09/21] /dev/mem: Add kernel config option to omit this
device.
Omitting this device prevents software from accessing the hardware directly, which can cause trouble if the kernel accesses the same hardware.
It also saves some space on embedded systems.
---
arch/x86/Kconfig.debug | 1 +
drivers/char/Kconfig | 10 ++++++++++
drivers/char/mem.c | 17 +++++++++++++++++
3 files changed, 28 insertions(+), 0 deletions(-)
diff --git a/arch/x86/Kconfig.debug b/arch/x86/Kconfig.debug
index bf56e17..0c97d51 100644
--- a/arch/x86/Kconfig.debug
+++ b/arch/x86/Kconfig.debug
@@ -7,6 +7,7 @@ source "lib/Kconfig.debug"
config STRICT_DEVMEM
bool "Filter access to /dev/mem"
+ depends on DEVMEM
---help---
If this option is disabled, you allow userspace (root) access to all
of memory, including kernel and userspace memory. Accidental
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 4364303..a2e462e 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -6,6 +6,16 @@ menu "Character devices"
source "drivers/tty/Kconfig"
+config DEVMEM
+ bool "/dev/mem virtual device support"
+ default y
+ help
+ Say Y here if you want to support the /dev/mem device.
+ Some X server drivers access the video hardware using this device.
+ Accessing hardware directly from user space can be useful in some
+ cases, but it is not without risks.
+ When in doubt, say "N".
+
config DEVKMEM
bool "/dev/kmem virtual device support"
default y
diff --git a/drivers/char/mem.c b/drivers/char/mem.c
index 1451790..a6020ca 100644
--- a/drivers/char/mem.c
+++ b/drivers/char/mem.c
@@ -87,6 +87,8 @@ void __weak unxlate_dev_mem_ptr(unsigned long phys, void *addr)
{
}
+#ifdef CONFIG_DEVMEM
+
/*
* This funcion reads the *physical* memory. The f_pos points directly to the
* memory location.
@@ -210,6 +212,10 @@ static ssize_t write_mem(struct file *file, const char __user *buf,
return written;
}
+#endif
+
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM)
+
int __weak phys_mem_access_prot_allowed(struct file *file,
unsigned long pfn, unsigned long size, pgprot_t *vma_prot)
{
@@ -331,6 +337,8 @@ static int mmap_mem(struct file *file, struct vm_area_struct *vma)
return 0;
}
+#endif
+
#ifdef CONFIG_DEVKMEM
static int mmap_kmem(struct file *file, struct vm_area_struct *vma)
{
@@ -694,6 +702,7 @@ static loff_t null_lseek(struct file *file, loff_t offset, int orig)
return file->f_pos = 0;
}
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || defined(CONFIG_DEVPORT)
/*
* The memory devices use the full 32/64 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
@@ -726,11 +735,15 @@ static loff_t memory_lseek(struct file *file, loff_t offset, int orig)
mutex_unlock(&file->f_path.dentry->d_inode->i_mutex);
return ret;
}
+#endif
+#if defined(CONFIG_DEVMEM) || defined(CONFIG_DEVKMEM) || \
+ defined(CONFIG_DEVPORT) || defined(CONFIG_CRASH_DUMP)
static int open_port(struct inode * inode, struct file * filp)
{
return capable(CAP_SYS_RAWIO) ? 0 : -EPERM;
}
+#endif
#define zero_lseek null_lseek
#define full_lseek null_lseek
@@ -740,6 +753,7 @@ static int open_port(struct inode * inode, struct file * filp)
#define open_kmem open_mem
#define open_oldmem open_mem
+#ifdef CONFIG_DEVMEM
static const struct file_operations mem_fops = {
.llseek = memory_lseek,
.read = read_mem,
@@ -748,6 +762,7 @@ static const struct file_operations mem_fops = {
.open = open_mem,
.get_unmapped_area = get_unmapped_area_mem,
};
+#endif
#ifdef CONFIG_DEVKMEM
static const struct file_operations kmem_fops = {
@@ -851,7 +866,9 @@ static const struct memdev {
const struct file_operations *fops;
struct backing_dev_info *dev_info;
} devlist[] = {
+#ifdef CONFIG_DEVMEM
[1] = { "mem", 0, &mem_fops, &directly_mappable_cdev_bdi },
+#endif
#ifdef CONFIG_DEVKMEM
[2] = { "kmem", 0, &kmem_fops, &directly_mappable_cdev_bdi },
#endif
--
1.7.5.4

@ -0,0 +1,306 @@
From ca40c7542f0cd0e0dfa074bd4ccefc04b8561427 Mon Sep 17 00:00:00 2001
From: Maarten ter Huurne <maarten@treewalker.org>
Date: Tue, 2 Aug 2011 10:26:09 +0200
Subject: [PATCH 10/21] cpufreq_stats: Support runtime changes to frequency
table.
---
drivers/cpufreq/cpufreq_stats.c | 161 ++++++++++++++++++++-------------------
1 files changed, 83 insertions(+), 78 deletions(-)
diff --git a/drivers/cpufreq/cpufreq_stats.c b/drivers/cpufreq/cpufreq_stats.c
index c5072a9..95f6eb9 100644
--- a/drivers/cpufreq/cpufreq_stats.c
+++ b/drivers/cpufreq/cpufreq_stats.c
@@ -21,6 +21,7 @@
#include <linux/kobject.h>
#include <linux/spinlock.h>
#include <linux/notifier.h>
+#include <linux/string.h>
#include <asm/cputime.h>
static spinlock_t cpufreq_stats_lock;
@@ -37,7 +38,7 @@ struct cpufreq_stats {
unsigned long long last_time;
unsigned int max_state;
unsigned int state_num;
- unsigned int last_index;
+ int last_index;
cputime64_t *time_in_state;
unsigned int *freq_table;
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
@@ -60,7 +61,7 @@ static int cpufreq_stats_update(unsigned int cpu)
cur_time = get_jiffies_64();
spin_lock(&cpufreq_stats_lock);
stat = per_cpu(cpufreq_stats_table, cpu);
- if (stat->time_in_state)
+ if (stat->time_in_state && stat->last_index != -1)
stat->time_in_state[stat->last_index] =
cputime64_add(stat->time_in_state[stat->last_index],
cputime_sub(cur_time, stat->last_time));
@@ -83,7 +84,7 @@ static ssize_t show_time_in_state(struct cpufreq_policy *policy, char *buf)
ssize_t len = 0;
int i;
struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
- if (!stat)
+ if (!stat || !stat->time_in_state)
return 0;
cpufreq_stats_update(stat->cpu);
for (i = 0; i < stat->state_num; i++) {
@@ -101,7 +102,7 @@ static ssize_t show_trans_table(struct cpufreq_policy *policy, char *buf)
int i, j;
struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, policy->cpu);
- if (!stat)
+ if (!stat || !stat->trans_table)
return 0;
cpufreq_stats_update(stat->cpu);
len += snprintf(buf + len, PAGE_SIZE - len, " From : To\n");
@@ -160,63 +161,35 @@ static struct attribute_group stats_attr_group = {
static int freq_table_get_index(struct cpufreq_stats *stat, unsigned int freq)
{
int index;
- for (index = 0; index < stat->max_state; index++)
- if (stat->freq_table[index] == freq)
- return index;
+ if (stat->freq_table)
+ for (index = 0; index < stat->max_state; index++)
+ if (stat->freq_table[index] == freq)
+ return index;
return -1;
}
-/* should be called late in the CPU removal sequence so that the stats
- * memory is still available in case someone tries to use it.
- */
static void cpufreq_stats_free_table(unsigned int cpu)
{
struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu);
+ struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
+ if (policy && policy->cpu == cpu)
+ sysfs_remove_group(&policy->kobj, &stats_attr_group);
if (stat) {
kfree(stat->time_in_state);
kfree(stat);
}
per_cpu(cpufreq_stats_table, cpu) = NULL;
-}
-
-/* must be called early in the CPU removal sequence (before
- * cpufreq_remove_dev) so that policy is still valid.
- */
-static void cpufreq_stats_free_sysfs(unsigned int cpu)
-{
- struct cpufreq_policy *policy = cpufreq_cpu_get(cpu);
- if (policy && policy->cpu == cpu)
- sysfs_remove_group(&policy->kobj, &stats_attr_group);
if (policy)
cpufreq_cpu_put(policy);
}
-static int cpufreq_stats_create_table(struct cpufreq_policy *policy,
+static int cpufreq_stats_update_table(struct cpufreq_policy *policy,
struct cpufreq_frequency_table *table)
{
- unsigned int i, j, count = 0, ret = 0;
- struct cpufreq_stats *stat;
- struct cpufreq_policy *data;
+ unsigned int i, j, count = 0;
unsigned int alloc_size;
unsigned int cpu = policy->cpu;
- if (per_cpu(cpufreq_stats_table, cpu))
- return -EBUSY;
- stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL);
- if ((stat) == NULL)
- return -ENOMEM;
-
- data = cpufreq_cpu_get(cpu);
- if (data == NULL) {
- ret = -EINVAL;
- goto error_get_fail;
- }
-
- ret = sysfs_create_group(&data->kobj, &stats_attr_group);
- if (ret)
- goto error_out;
-
- stat->cpu = cpu;
- per_cpu(cpufreq_stats_table, cpu) = stat;
+ struct cpufreq_stats *stat = per_cpu(cpufreq_stats_table, cpu);
for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
unsigned int freq = table[i].frequency;
@@ -225,40 +198,73 @@ static int cpufreq_stats_create_table(struct cpufreq_policy *policy,
count++;
}
+ if (stat->max_state != count) {
+ stat->max_state = count;
+ kfree(stat->time_in_state);
+ stat->time_in_state = NULL;
+ }
alloc_size = count * sizeof(int) + count * sizeof(cputime64_t);
-
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
alloc_size += count * count * sizeof(int);
#endif
- stat->max_state = count;
- stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
- if (!stat->time_in_state) {
- ret = -ENOMEM;
- goto error_out;
- }
- stat->freq_table = (unsigned int *)(stat->time_in_state + count);
-
+ if (stat->time_in_state) {
+ memset(stat->time_in_state, 0, alloc_size);
+ } else {
+ stat->time_in_state = kzalloc(alloc_size, GFP_KERNEL);
+ if (!stat->time_in_state)
+ return -ENOMEM;
+ stat->freq_table = (unsigned int *)(
+ stat->time_in_state + count);
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
- stat->trans_table = stat->freq_table + count;
+ stat->trans_table = stat->freq_table + count;
#endif
+ }
+
j = 0;
- for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
- unsigned int freq = table[i].frequency;
- if (freq == CPUFREQ_ENTRY_INVALID)
- continue;
- if (freq_table_get_index(stat, freq) == -1)
- stat->freq_table[j++] = freq;
+ if (stat->freq_table) {
+ for (i = 0; table[i].frequency != CPUFREQ_TABLE_END; i++) {
+ unsigned int freq = table[i].frequency;
+ if (freq == CPUFREQ_ENTRY_INVALID)
+ continue;
+ if (freq_table_get_index(stat, freq) == -1)
+ stat->freq_table[j++] = freq;
+ }
}
stat->state_num = j;
spin_lock(&cpufreq_stats_lock);
stat->last_time = get_jiffies_64();
stat->last_index = freq_table_get_index(stat, policy->cur);
spin_unlock(&cpufreq_stats_lock);
+ return 0;
+}
+
+static int cpufreq_stats_create_table(struct cpufreq_policy *policy,
+ struct cpufreq_frequency_table *table)
+{
+ unsigned int ret = 0;
+ struct cpufreq_stats *stat;
+ struct cpufreq_policy *data;
+ unsigned int cpu = policy->cpu;
+
+ stat = kzalloc(sizeof(struct cpufreq_stats), GFP_KERNEL);
+ if ((stat) == NULL)
+ return -ENOMEM;
+
+ data = cpufreq_cpu_get(cpu);
+ if (data == NULL) {
+ ret = -EINVAL;
+ goto error_out;
+ }
+ ret = sysfs_create_group(&data->kobj, &stats_attr_group);
cpufreq_cpu_put(data);
+ if (ret)
+ goto error_out;
+
+ stat->cpu = cpu;
+ per_cpu(cpufreq_stats_table, cpu) = stat;
+
return 0;
error_out:
- cpufreq_cpu_put(data);
-error_get_fail:
kfree(stat);
per_cpu(cpufreq_stats_table, cpu) = NULL;
return ret;
@@ -276,10 +282,12 @@ static int cpufreq_stat_notifier_policy(struct notifier_block *nb,
table = cpufreq_frequency_get_table(cpu);
if (!table)
return 0;
- ret = cpufreq_stats_create_table(policy, table);
- if (ret)
- return ret;
- return 0;
+ if (!per_cpu(cpufreq_stats_table, cpu)) {
+ ret = cpufreq_stats_create_table(policy, table);
+ if (ret)
+ return ret;
+ }
+ return cpufreq_stats_update_table(policy, table);
}
static int cpufreq_stat_notifier_trans(struct notifier_block *nb,
@@ -299,21 +307,23 @@ static int cpufreq_stat_notifier_trans(struct notifier_block *nb,
old_index = stat->last_index;
new_index = freq_table_get_index(stat, freq->new);
- /* We can't do stat->time_in_state[-1]= .. */
- if (old_index == -1 || new_index == -1)
- return 0;
-
cpufreq_stats_update(freq->cpu);
-
if (old_index == new_index)
return 0;
+ if (new_index == -1)
+ return 0;
+
spin_lock(&cpufreq_stats_lock);
stat->last_index = new_index;
+ if (old_index != -1) {
#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
- stat->trans_table[old_index * stat->max_state + new_index]++;
+ if (stat->trans_table)
+ stat->trans_table[old_index * stat->max_state +
+ new_index]++;
#endif
- stat->total_trans++;
+ stat->total_trans++;
+ }
spin_unlock(&cpufreq_stats_lock);
return 0;
}
@@ -329,9 +339,6 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
case CPU_ONLINE_FROZEN:
cpufreq_update_policy(cpu);
break;
- case CPU_DOWN_PREPARE:
- cpufreq_stats_free_sysfs(cpu);
- break;
case CPU_DEAD:
case CPU_DEAD_FROZEN:
cpufreq_stats_free_table(cpu);
@@ -340,10 +347,9 @@ static int __cpuinit cpufreq_stat_cpu_callback(struct notifier_block *nfb,
return NOTIFY_OK;
}
-/* priority=1 so this will get called before cpufreq_remove_dev */
-static struct notifier_block cpufreq_stat_cpu_notifier __refdata = {
+static struct notifier_block cpufreq_stat_cpu_notifier __refdata =
+{
.notifier_call = cpufreq_stat_cpu_callback,
- .priority = 1,
};
static struct notifier_block notifier_policy_block = {
@@ -390,7 +396,6 @@ static void __exit cpufreq_stats_exit(void)
unregister_hotcpu_notifier(&cpufreq_stat_cpu_notifier);
for_each_online_cpu(cpu) {
cpufreq_stats_free_table(cpu);
- cpufreq_stats_free_sysfs(cpu);
}
}
--
1.7.5.4

@ -0,0 +1,358 @@
From 27ff621cd9a5347efda4be502abbef13a99146ce Mon Sep 17 00:00:00 2001
From: Maarten ter Huurne <maarten@treewalker.org>
Date: Sun, 29 Aug 2010 08:11:00 +0200
Subject: [PATCH 11/21] MIPS: JZ4740: Added setting of PLL rate and main
dividers.
This functionality makes a cpufreq driver possible.
Squashed version of the development done in the jz-2.6.39 branch.
---
arch/mips/jz4740/clock.c | 230 ++++++++++++++++++++++++++++++++++++++++++++--
arch/mips/jz4740/clock.h | 4 +
2 files changed, 224 insertions(+), 10 deletions(-)
diff --git a/arch/mips/jz4740/clock.c b/arch/mips/jz4740/clock.c
index 118a8a5..da423d1 100644
--- a/arch/mips/jz4740/clock.c
+++ b/arch/mips/jz4740/clock.c
@@ -1,5 +1,8 @@
/*
+ * Copyright (c) 2006-2007, Ingenic Semiconductor Inc.
* Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de>
+ * Copyright (c) 2010, Ulrich Hecht <ulrich.hecht@gmail.com>
+ * Copyright (c) 2010, Maarten ter Huurne <maarten@treewalker.org>
* JZ4740 SoC clock support
*
* This program is free software; you can redistribute it and/or modify it
@@ -41,16 +44,20 @@
#define JZ_CLOCK_CTRL_I2S_SRC_PLL BIT(31)
#define JZ_CLOCK_CTRL_KO_ENABLE BIT(30)
#define JZ_CLOCK_CTRL_UDC_SRC_PLL BIT(29)
-#define JZ_CLOCK_CTRL_UDIV_MASK 0x1f800000
#define JZ_CLOCK_CTRL_CHANGE_ENABLE BIT(22)
#define JZ_CLOCK_CTRL_PLL_HALF BIT(21)
-#define JZ_CLOCK_CTRL_LDIV_MASK 0x001f0000
#define JZ_CLOCK_CTRL_UDIV_OFFSET 23
#define JZ_CLOCK_CTRL_LDIV_OFFSET 16
#define JZ_CLOCK_CTRL_MDIV_OFFSET 12
#define JZ_CLOCK_CTRL_PDIV_OFFSET 8
#define JZ_CLOCK_CTRL_HDIV_OFFSET 4
#define JZ_CLOCK_CTRL_CDIV_OFFSET 0
+#define JZ_CLOCK_CTRL_UDIV_MASK (0x3f << JZ_CLOCK_CTRL_UDIV_OFFSET)
+#define JZ_CLOCK_CTRL_LDIV_MASK (0x1f << JZ_CLOCK_CTRL_LDIV_OFFSET)
+#define JZ_CLOCK_CTRL_MDIV_MASK (0x0f << JZ_CLOCK_CTRL_MDIV_OFFSET)
+#define JZ_CLOCK_CTRL_PDIV_MASK (0x0f << JZ_CLOCK_CTRL_PDIV_OFFSET)
+#define JZ_CLOCK_CTRL_HDIV_MASK (0x0f << JZ_CLOCK_CTRL_HDIV_OFFSET)
+#define JZ_CLOCK_CTRL_CDIV_MASK (0x0f << JZ_CLOCK_CTRL_CDIV_OFFSET)
#define JZ_CLOCK_GATE_UART0 BIT(0)
#define JZ_CLOCK_GATE_TCU BIT(1)
@@ -90,6 +97,7 @@
#define JZ_CLOCK_PLL_M_OFFSET 23
#define JZ_CLOCK_PLL_N_OFFSET 18
#define JZ_CLOCK_PLL_OD_OFFSET 16
+#define JZ_CLOCK_PLL_STABILIZE_OFFSET 0
#define JZ_CLOCK_LOW_POWER_MODE_DOZE BIT(2)
#define JZ_CLOCK_LOW_POWER_MODE_SLEEP BIT(0)
@@ -97,10 +105,15 @@
#define JZ_CLOCK_SLEEP_CTRL_SUSPEND_UHC BIT(7)
#define JZ_CLOCK_SLEEP_CTRL_ENABLE_UDC BIT(6)
+#define JZ_REG_EMC_RTCNT 0x88
+#define JZ_REG_EMC_RTCOR 0x8C
+
static void __iomem *jz_clock_base;
static spinlock_t jz_clock_lock;
static LIST_HEAD(jz_clocks);
+static void __iomem *jz_emc_base;
+
struct main_clk {
struct clk clk;
uint32_t div_offset;
@@ -204,25 +217,88 @@ static int jz_clk_ko_is_enabled(struct clk *clk)
return !!(jz_clk_reg_read(JZ_REG_CLOCK_CTRL) & JZ_CLOCK_CTRL_KO_ENABLE);
}
+static struct static_clk jz_clk_ext;
+
+static unsigned long jz_clk_pll_calc_rate(
+ unsigned int in_div, unsigned int feedback, unsigned int out_div)
+{
+ return ((jz_clk_ext.rate / in_div) * feedback) / out_div;
+}
+
+static void jz_clk_pll_calc_dividers(unsigned long rate,
+ unsigned int *in_div, unsigned int *feedback, unsigned int *out_div)
+{
+ unsigned int target;
+
+ /* The frequency after the input divider must be between 1 and 15 MHz.
+ The highest divider yields the best resolution. */
+ *in_div = jz_clk_ext.rate / 1000000;
+ if (*in_div >= 34)
+ *in_div = 33;
+
+ /* The frequency before the output divider must be between 100 and
+ 500 MHz. The lowest target rate is more energy efficient. */
+ if (rate < 25000000) {
+ *out_div = 4;
+ target = 25000000 * 4;
+ } else if (rate <= 50000000) {
+ *out_div = 4;
+ target = rate * 4;
+ } else if (rate <= 100000000) {
+ *out_div = 2;
+ target = rate * 2;
+ } else if (rate <= 500000000) {
+ *out_div = 1;
+ target = rate;
+ } else {
+ *out_div = 1;
+ target = 500000000;
+ }
+
+ /* Compute the feedback divider.
+ Since the divided input is at least 1 MHz and the target frequency
+ at most 500 MHz, the feedback will be at most 500 and will therefore
+ always fit in the 9-bit register.
+ Similarly, the divided input is at most 15 MHz and the target
+ frequency at least 100 MHz, so the feedback will be at least 6
+ where the minimum supported value is 2. */
+ *feedback = ((target / 1000) * *in_div) / (jz_clk_ext.rate / 1000);
+}
+
+static unsigned long jz_clk_pll_round_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned int in_div, feedback, out_div;
+ /* The PLL frequency must be a multiple of 24 MHz, since the LCD pixel
+ * clock must be exactly 12 MHz for the TV-out to work.
+ * TODO: A multiple of 12 MHz for the PLL would work if the PLL would
+ * not be divided by 2 before being passed to the set of derived
+ * clocks that includes the LCD pixel clock.
+ * TODO: Systemwide decisions like this should be made by the board
+ * support code, so add some kind of hook for that.
+ */
+ unsigned long rate24 = (rate / 24000000) * 24000000;
+
+ jz_clk_pll_calc_dividers(rate24, &in_div, &feedback, &out_div);
+ return jz_clk_pll_calc_rate(in_div, feedback, out_div);
+}
+
static const int pllno[] = {1, 2, 2, 4};
static unsigned long jz_clk_pll_get_rate(struct clk *clk)
{
uint32_t val;
- int m;
- int n;
- int od;
+ unsigned int in_div, feedback, out_div;
val = jz_clk_reg_read(JZ_REG_CLOCK_PLL);
if (val & JZ_CLOCK_PLL_BYPASS)
return clk_get_rate(clk->parent);
- m = ((val >> 23) & 0x1ff) + 2;
- n = ((val >> 18) & 0x1f) + 2;
- od = (val >> 16) & 0x3;
+ feedback = ((val >> 23) & 0x1ff) + 2;
+ in_div = ((val >> 18) & 0x1f) + 2;
+ out_div = pllno[(val >> 16) & 0x3];
- return ((clk_get_rate(clk->parent) / n) * m) / pllno[od];
+ return jz_clk_pll_calc_rate(in_div, feedback, out_div);
}
static unsigned long jz_clk_pll_half_get_rate(struct clk *clk)
@@ -235,7 +311,77 @@ static unsigned long jz_clk_pll_half_get_rate(struct clk *clk)
return jz_clk_pll_get_rate(clk->parent) >> 1;
}
-static const int jz_clk_main_divs[] = {1, 2, 3, 4, 6, 8, 12, 16, 24, 32};
+#define SDRAM_TREF 15625 /* Refresh period: 4096 refresh cycles/64ms */
+
+static void sdram_set_pll(unsigned int pllin)
+{
+ unsigned int ns, sdramclock;
+
+ ns = 1000000000 / pllin;
+ sdramclock = (SDRAM_TREF / ns) / 64 + 1;
+ if (sdramclock > 0xff) sdramclock = 0xff;
+ /* Set refresh registers */
+ writew(sdramclock, jz_emc_base + JZ_REG_EMC_RTCOR);
+ writew(sdramclock, jz_emc_base + JZ_REG_EMC_RTCNT);
+}
+
+static int jz_clk_pll_set_rate(struct clk *clk, unsigned long rate)
+{
+ unsigned int ctrl, plcr1;
+ unsigned int feedback, in_div, out_div, pllout, pllout2;
+
+ jz_clk_pll_calc_dividers(rate, &in_div, &feedback, &out_div);
+
+ ctrl = jz_clk_reg_read(JZ_REG_CLOCK_CTRL);
+ pllout = jz_clk_pll_calc_rate(in_div, feedback, out_div);
+ pllout2 = (ctrl & JZ_CLOCK_CTRL_PLL_HALF) ? pllout : (pllout / 2);
+
+ /* Init UHC clock */
+ writel(pllout2 / 48000000 - 1, jz_clock_base + JZ_REG_CLOCK_UHC);
+
+ plcr1 = ((feedback - 2) << JZ_CLOCK_PLL_M_OFFSET) |
+ ((in_div - 2) << JZ_CLOCK_PLL_N_OFFSET) |
+ ((out_div - 1) << JZ_CLOCK_PLL_OD_OFFSET) |
+ (0x20 << JZ_CLOCK_PLL_STABILIZE_OFFSET) |
+ JZ_CLOCK_PLL_ENABLED;
+
+ sdram_set_pll(pllout);
+
+ /* LCD pixclock */
+ writel(pllout2 / 12000000 - 1, jz_clock_base + JZ_REG_CLOCK_LCD);
+
+ /* configure PLL */
+ __asm__ __volatile__(
+ ".set noreorder\n\t"
+ ".align 5\n"
+ "sw %1,0(%0)\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ ".set reorder\n\t"
+ :
+ : "r" (jz_clock_base + JZ_REG_CLOCK_PLL), "r" (plcr1));
+
+ /* MtH: For some reason the MSC will have problems if this flag is not
+ restored, even though the MSC is supposedly the only divider
+ that is not affected by this flag. */
+ jz_clk_reg_set_bits(JZ_REG_CLOCK_CTRL, JZ_CLOCK_CTRL_CHANGE_ENABLE);
+
+ return 0;
+}
+
+static const unsigned int jz_clk_main_divs[] = {
+ 1, 2, 3, 4, 6, 8, 12, 16, 24, 32
+};
+static const unsigned int jz_clk_main_divs_inv[] = {
+ -1, 0, 1, 2, 3, -1, 4, -1, 5, -1, -1, -1, 6, -1, -1, -1,
+ 7, -1, -1, -1, -1, -1, -1, -1, 8, -1, -1, -1, -1, -1, -1, -1,
+ 9
+};
static unsigned long jz_clk_main_round_rate(struct clk *clk, unsigned long rate)
{
@@ -290,6 +436,64 @@ static int jz_clk_main_set_rate(struct clk *clk, unsigned long rate)
return 0;
}
+static struct main_clk jz_clk_cpu;
+
+int clk_main_set_dividers(bool immediate, unsigned int cdiv, unsigned int hdiv,
+ unsigned int mdiv, unsigned int pdiv)
+{
+ unsigned int cdiv_enc, hdiv_enc, mdiv_enc, pdiv_enc;
+ unsigned int ctrl;
+ unsigned int tmp, wait;
+
+ if (cdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) ||
+ hdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) ||
+ mdiv >= ARRAY_SIZE(jz_clk_main_divs_inv) ||
+ pdiv >= ARRAY_SIZE(jz_clk_main_divs_inv))
+ return -EINVAL;
+ cdiv_enc = jz_clk_main_divs_inv[cdiv];
+ hdiv_enc = jz_clk_main_divs_inv[hdiv];
+ mdiv_enc = jz_clk_main_divs_inv[mdiv];
+ pdiv_enc = jz_clk_main_divs_inv[pdiv];
+ if (cdiv_enc == (unsigned int)-1 ||
+ hdiv_enc == (unsigned int)-1 ||
+ mdiv_enc == (unsigned int)-1 ||
+ pdiv_enc == (unsigned int)-1)
+ return -EINVAL;
+
+ ctrl = jz_clk_reg_read(JZ_REG_CLOCK_CTRL);
+ ctrl &= ~(JZ_CLOCK_CTRL_CHANGE_ENABLE |
+ JZ_CLOCK_CTRL_CDIV_MASK | JZ_CLOCK_CTRL_HDIV_MASK |
+ JZ_CLOCK_CTRL_MDIV_MASK | JZ_CLOCK_CTRL_PDIV_MASK);
+ if (immediate) ctrl |= JZ_CLOCK_CTRL_CHANGE_ENABLE;
+ ctrl |= (cdiv_enc << JZ_CLOCK_CTRL_CDIV_OFFSET) |
+ (hdiv_enc << JZ_CLOCK_CTRL_HDIV_OFFSET) |
+ (mdiv_enc << JZ_CLOCK_CTRL_MDIV_OFFSET) |
+ (pdiv_enc << JZ_CLOCK_CTRL_PDIV_OFFSET);
+
+ /* set dividers */
+ /* delay loops lifted from the old Ingenic cpufreq driver */
+ wait = ((clk_get_rate(&jz_clk_cpu.clk) / 1000000) * 500) / 1000;
+ __asm__ __volatile__(
+ ".set noreorder\n\t"
+ ".align 5\n"
+ "sw %2,0(%1)\n\t"
+ "li %0,0\n\t"
+ "1:\n\t"
+ "bne %0,%3,1b\n\t"
+ "addi %0, 1\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ "nop\n\t"
+ ".set reorder\n\t"
+ : "=r" (tmp)
+ : "r" (jz_clock_base + JZ_REG_CLOCK_CTRL), "r" (ctrl),
+ "r" (wait));
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(clk_main_set_dividers);
+
static struct clk_ops jz_clk_static_ops = {
.get_rate = jz_clk_static_get_rate,
.enable = jz_clk_enable_gating,
@@ -307,6 +511,8 @@ static struct static_clk jz_clk_ext = {
static struct clk_ops jz_clk_pll_ops = {
.get_rate = jz_clk_pll_get_rate,
+ .set_rate = jz_clk_pll_set_rate,
+ .round_rate = jz_clk_pll_round_rate,
};
static struct clk jz_clk_pll = {
@@ -897,6 +1103,10 @@ static int jz4740_clock_init(void)
if (!jz_clock_base)
return -EBUSY;
+ jz_emc_base = ioremap(JZ4740_EMC_BASE_ADDR, 0x100);
+ if (!jz_emc_base)
+ return -EBUSY;
+
spin_lock_init(&jz_clock_lock);
jz_clk_ext.rate = jz4740_clock_bdata.ext_rate;
diff --git a/arch/mips/jz4740/clock.h b/arch/mips/jz4740/clock.h
index 5d07499..cc8d1db 100644
--- a/arch/mips/jz4740/clock.h
+++ b/arch/mips/jz4740/clock.h
@@ -17,6 +17,7 @@
#define __MIPS_JZ4740_CLOCK_H__
#include <linux/list.h>
+#include <linux/types.h>
struct jz4740_clock_board_data {
unsigned long ext_rate;
@@ -63,6 +64,9 @@ struct clk {
int clk_is_enabled(struct clk *clk);
+int clk_main_set_dividers(bool immediate, unsigned int cdiv, unsigned int hdiv,
+ unsigned int mdiv, unsigned int pdiv);
+
#ifdef CONFIG_DEBUG_FS
void jz4740_clock_debugfs_init(void);
void jz4740_clock_debugfs_add_clk(struct clk *clk);
--
1.7.5.4

@ -0,0 +1,310 @@
From d0f0d5739a31c12d349980ed05a670fa1e84696d Mon Sep 17 00:00:00 2001
From: Maarten ter Huurne <maarten@treewalker.org>
Date: Wed, 16 Mar 2011 03:16:04 +0100
Subject: [PATCH 12/21] MIPS: JZ4740: Add cpufreq support.
This is a squashed version of Uli's driver that was further developed in the opendingux-kernel repository.
---
arch/mips/Kconfig | 1 +
arch/mips/jz4740/Makefile | 1 +
arch/mips/jz4740/cpufreq.c | 226 ++++++++++++++++++++++++++++++++++++++
arch/mips/kernel/cpufreq/Kconfig | 13 ++-
4 files changed, 240 insertions(+), 1 deletions(-)
create mode 100644 arch/mips/jz4740/cpufreq.c
diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index d46f1da..8128df7 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -209,6 +209,7 @@ config MACH_JZ4740
select HAVE_PWM
select HAVE_CLK
select GENERIC_IRQ_CHIP
+ select CPU_SUPPORTS_CPUFREQ
config LANTIQ
bool "Lantiq based platforms"
diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile
index a9dff33..15f828e 100644
--- a/arch/mips/jz4740/Makefile
+++ b/arch/mips/jz4740/Makefile
@@ -16,5 +16,6 @@ obj-$(CONFIG_JZ4740_QI_LB60) += board-qi_lb60.o
# PM support
obj-$(CONFIG_PM) += pm.o
+obj-$(CONFIG_CPU_FREQ_JZ) += cpufreq.o
ccflags-y := -Werror -Wall
diff --git a/arch/mips/jz4740/cpufreq.c b/arch/mips/jz4740/cpufreq.c
new file mode 100644
index 0000000..aa41e9f
--- /dev/null
+++ b/arch/mips/jz4740/cpufreq.c
@@ -0,0 +1,226 @@
+/*
+ * linux/arch/mips/jz4740/cpufreq.c
+ *
+ * cpufreq driver for JZ4740
+ *
+ * Copyright (c) 2010 Ulrich Hecht <ulrich.hecht@gmail.com>
+ * Copyright (c) 2010 Maarten ter Huurne <maarten@treewalker.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+
+#include <linux/cpufreq.h>
+
+#include <linux/clk.h>
+#include <asm/mach-jz4740/base.h>
+
+#include "clock.h"
+
+#define DEBUG_CPUFREQ
+
+#ifdef DEBUG_CPUFREQ
+#define dprintk(X...) printk(KERN_INFO X)
+#else
+#define dprintk(X...) do { } while(0)
+#endif
+
+#define HCLK_MIN 30000
+/* TODO: The maximum MCLK most likely depends on the SDRAM chips used,
+ so it is board-specific. */
+#define MCLK_MAX 140000
+
+/* Same as jz_clk_main_divs, but with 24 and 32 removed because the hardware
+ spec states those dividers must not be used for CCLK or HCLK. */
+static const unsigned int jz4740_freq_cpu_divs[] = {1, 2, 3, 4, 6, 8, 12, 16};
+
+struct jz4740_freq_percpu_info {
+ unsigned int pll_rate;
+ struct cpufreq_frequency_table table[
+ ARRAY_SIZE(jz4740_freq_cpu_divs) + 1];
+};
+
+static struct clk *pll;
+static struct clk *cclk;
+
+static struct jz4740_freq_percpu_info jz4740_freq_info;
+
+static struct cpufreq_driver cpufreq_jz4740_driver;
+
+static void jz4740_freq_fill_table(struct cpufreq_policy *policy,
+ unsigned int pll_rate)
+{
+ struct cpufreq_frequency_table *table = &jz4740_freq_info.table[0];
+ int i;
+
+#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
+ /* for showing /sys/devices/system/cpu/cpuX/cpufreq/stats/ */
+ static bool init = false;
+ if (init)
+ cpufreq_frequency_table_put_attr(policy->cpu);
+ else
+ init = true;
+#endif
+
+ jz4740_freq_info.pll_rate = pll_rate;
+
+ for (i = 0; i < ARRAY_SIZE(jz4740_freq_cpu_divs); i++) {
+ unsigned int freq = pll_rate / jz4740_freq_cpu_divs[i];
+ if (freq < HCLK_MIN) break;
+ table[i].index = i;
+ table[i].frequency = freq;
+ }
+ table[i].index = i;
+ table[i].frequency = CPUFREQ_TABLE_END;
+
+ policy->min = table[i - 1].frequency;
+ policy->max = table[0].frequency;
+
+#ifdef CONFIG_CPU_FREQ_STAT_DETAILS
+ cpufreq_frequency_table_get_attr(table, policy->cpu);
+#endif
+}
+
+static unsigned int jz4740_freq_get(unsigned int cpu)
+{
+ return clk_get_rate(cclk) / 1000;
+}
+
+static int jz4740_freq_verify(struct cpufreq_policy *policy)
+{
+ unsigned int new_pll;
+
+ cpufreq_verify_within_limits(policy, policy->cpuinfo.min_freq,
+ policy->cpuinfo.max_freq);
+
+ new_pll = clk_round_rate(pll, policy->max * 1000) / 1000;
+ if (jz4740_freq_info.pll_rate != new_pll)
+ jz4740_freq_fill_table(policy, new_pll);
+
+ return 0;
+}
+
+static int jz4740_freq_target(struct cpufreq_policy *policy,
+ unsigned int target_freq,
+ unsigned int relation)
+{
+ struct cpufreq_frequency_table *table = &jz4740_freq_info.table[0];
+ struct cpufreq_freqs freqs;
+ unsigned int new_index = 0;
+ unsigned int old_pll = clk_get_rate(pll) / 1000;
+ unsigned int new_pll = jz4740_freq_info.pll_rate;
+ int ret = 0;
+
+ if (cpufreq_frequency_table_target(policy, table,
+ target_freq, relation, &new_index))
+ return -EINVAL;
+ freqs = (struct cpufreq_freqs) {
+ .old = jz4740_freq_get(policy->cpu),
+ .new = table[new_index].frequency,
+ .cpu = policy->cpu,
+ .flags = cpufreq_jz4740_driver.flags,
+ };
+ if (freqs.new != freqs.old || new_pll != old_pll) {
+ unsigned int cdiv, hdiv, mdiv, pdiv;
+ cdiv = jz4740_freq_cpu_divs[new_index];
+ hdiv = (cdiv == 3 || cdiv == 6) ? cdiv * 2 : cdiv * 3;
+ while (new_pll < HCLK_MIN * hdiv)
+ hdiv -= cdiv;
+ mdiv = hdiv;
+ if (new_pll > MCLK_MAX * mdiv) {
+ /* 4,4 performs better than 3,6 */
+ if (new_pll > MCLK_MAX * 4)
+ mdiv *= 2;
+ else
+ hdiv = mdiv = cdiv * 4;
+ }
+ pdiv = mdiv;
+ dprintk(KERN_INFO "%s: cclk %p, setting from %d to %d, "
+ "dividers %d, %d, %d, %d\n",
+ __FUNCTION__, cclk, freqs.old, freqs.new,
+ cdiv, hdiv, mdiv, pdiv);
+ cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
+ ret = clk_main_set_dividers(new_pll == old_pll,
+ cdiv, hdiv, mdiv, pdiv);
+ if (ret) {
+ dprintk(KERN_INFO "failed to set dividers\n");
+ } else if (new_pll != old_pll) {
+ dprintk(KERN_INFO "%s: pll %p, setting from %d to %d\n",
+ __FUNCTION__, pll, old_pll, new_pll);
+ ret = clk_set_rate(pll, new_pll * 1000);
+ }
+ cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
+ }
+
+ return ret;
+}
+
+static int jz4740_cpufreq_driver_init(struct cpufreq_policy *policy)
+{
+ int ret;
+
+ dprintk(KERN_INFO "Jz4740 cpufreq driver\n");
+
+ if (policy->cpu != 0)
+ return -EINVAL;
+
+ pll = clk_get(NULL, "pll");
+ if (IS_ERR(pll)) {
+ ret = PTR_ERR(pll);
+ goto err_exit;
+ }
+
+ cclk = clk_get(NULL, "cclk");
+ if (IS_ERR(cclk)) {
+ ret = PTR_ERR(cclk);
+ goto err_clk_put_pll;
+ }
+
+ policy->cpuinfo.min_freq = HCLK_MIN;
+ policy->cpuinfo.max_freq = 500000;
+ policy->cpuinfo.transition_latency = 100000; /* in nanoseconds */
+ policy->cur = jz4740_freq_get(policy->cpu);
+ policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
+ /* min and max are set by jz4740_freq_fill_table() */
+
+ jz4740_freq_fill_table(policy, clk_get_rate(pll) / 1000 /* in kHz */);
+
+ return 0;
+
+err_clk_put_pll:
+ clk_put(pll);
+err_exit:
+ return ret;
+}
+
+static struct cpufreq_driver cpufreq_jz4740_driver = {
+ .init = jz4740_cpufreq_driver_init,
+ .verify = jz4740_freq_verify,
+ .target = jz4740_freq_target,
+ .get = jz4740_freq_get,
+ .name = "jz4740",
+};
+
+static int __init jz4740_cpufreq_init(void)
+{
+ return cpufreq_register_driver(&cpufreq_jz4740_driver);
+}
+
+static void __exit jz4740_cpufreq_exit(void)
+{
+ cpufreq_unregister_driver(&cpufreq_jz4740_driver);
+}
+
+module_init(jz4740_cpufreq_init);
+module_exit(jz4740_cpufreq_exit);
+
+MODULE_AUTHOR("Ulrich Hecht <ulrich.hecht@gmail.com>, "
+ "Maarten ter Huurne <maarten@treewalker.org>");
+MODULE_DESCRIPTION("cpufreq driver for Jz4740");
+MODULE_LICENSE("GPL");
diff --git a/arch/mips/kernel/cpufreq/Kconfig b/arch/mips/kernel/cpufreq/Kconfig
index 58c601e..11af8e8 100644
--- a/arch/mips/kernel/cpufreq/Kconfig
+++ b/arch/mips/kernel/cpufreq/Kconfig
@@ -8,7 +8,7 @@ config MIPS_EXTERNAL_TIMER
config MIPS_CPUFREQ
bool
default y
- depends on CPU_SUPPORTS_CPUFREQ && MIPS_EXTERNAL_TIMER
+ depends on CPU_SUPPORTS_CPUFREQ
if MIPS_CPUFREQ
@@ -24,6 +24,7 @@ config LOONGSON2_CPUFREQ
tristate "Loongson2 CPUFreq Driver"
select CPU_FREQ_TABLE
depends on MIPS_CPUFREQ
+ depends on MIPS_EXTERNAL_TIMER
help
This option adds a CPUFreq driver for loongson processors which
support software configurable cpu frequency.
@@ -34,6 +35,16 @@ config LOONGSON2_CPUFREQ
If in doubt, say N.
+config CPU_FREQ_JZ
+ tristate "CPUfreq driver for JZ CPUs"
+ select CPU_FREQ_TABLE
+ depends on MACH_JZ4740
+ default n
+ help
+ This enables the CPUfreq driver for JZ CPUs.
+
+ If in doubt, say N.
+
endif # CPU_FREQ
endmenu
--
1.7.5.4

@ -0,0 +1,134 @@
From b95144c1b702f98c7902c75beb83f323701eb7c5 Mon Sep 17 00:00:00 2001
From: Maarten ter Huurne <maarten@treewalker.org>
Date: Sun, 19 Jun 2011 10:57:18 +0200
Subject: [PATCH 13/21] MMC: JZ4740: Added support for CPU frequency changing.
The MSC device clock is stopped before the frequency change.
After the change a new divider is computed and the clock is restarted.
Also the frequency change is postponed if an I/O operation is in progress.
---
drivers/mmc/host/jz4740_mmc.c | 69 +++++++++++++++++++++++++++++++++++++++-
1 files changed, 67 insertions(+), 2 deletions(-)
diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c
index 74218ad..6e40f1b 100644
--- a/drivers/mmc/host/jz4740_mmc.c
+++ b/drivers/mmc/host/jz4740_mmc.c
@@ -23,6 +23,7 @@
#include <linux/delay.h>
#include <linux/scatterlist.h>
#include <linux/clk.h>
+#include <linux/cpufreq.h>
#include <linux/bitops.h>
#include <linux/gpio.h>
@@ -685,6 +686,60 @@ static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable)
jz4740_mmc_set_irq_enabled(host, JZ_MMC_IRQ_SDIO, enable);
}
+#ifdef CONFIG_CPU_FREQ
+
+static struct jz4740_mmc_host *cpufreq_host;
+
+static int jz4740_mmc_cpufreq_transition(struct notifier_block *nb,
+ unsigned long val, void *data)
+{
+ /* TODO: We only have to take action when the PLL freq changes:
+ the main dividers have no influence on the MSC device clock. */
+
+ if (val == CPUFREQ_PRECHANGE) {
+ mmc_claim_host(cpufreq_host->mmc);
+ clk_disable(cpufreq_host->clk);
+ } else if (val == CPUFREQ_POSTCHANGE) {
+ struct mmc_ios *ios = &cpufreq_host->mmc->ios;
+ if (ios->clock)
+ jz4740_mmc_set_clock_rate(cpufreq_host, ios->clock);
+ if (ios->power_mode != MMC_POWER_OFF)
+ clk_enable(cpufreq_host->clk);
+ mmc_release_host(cpufreq_host->mmc);
+ }
+ return 0;
+}
+
+static struct notifier_block jz4740_mmc_cpufreq_nb = {
+ .notifier_call = jz4740_mmc_cpufreq_transition,
+};
+
+static inline int jz4740_mmc_cpufreq_register(struct jz4740_mmc_host *host)
+{
+ cpufreq_host = host;
+ return cpufreq_register_notifier(&jz4740_mmc_cpufreq_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+static inline void jz4740_mmc_cpufreq_unregister(void)
+{
+ cpufreq_unregister_notifier(&jz4740_mmc_cpufreq_nb,
+ CPUFREQ_TRANSITION_NOTIFIER);
+}
+
+#else
+
+static inline int jz4740_mmc_cpufreq_register(struct jz4740_mmc_host *host)
+{
+ return 0;
+}
+
+static inline void jz4740_mmc_cpufreq_unregister(void)
+{
+}
+
+#endif
+
static const struct mmc_host_ops jz4740_mmc_ops = {
.request = jz4740_mmc_request,
.set_ios = jz4740_mmc_set_ios,
@@ -834,11 +889,18 @@ static int __devinit jz4740_mmc_probe(struct platform_device* pdev)
goto err_free_host;
}
+ ret = jz4740_mmc_cpufreq_register(host);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Failed to register cpufreq transition notifier\n");
+ goto err_clk_put;
+ }
+
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!host->mem) {
ret = -ENOENT;
dev_err(&pdev->dev, "Failed to get base platform memory\n");
- goto err_clk_put;
+ goto err_cpufreq_unreg;
}
host->mem = request_mem_region(host->mem->start,
@@ -846,7 +908,7 @@ static int __devinit jz4740_mmc_probe(struct platform_device* pdev)
if (!host->mem) {
ret = -EBUSY;
dev_err(&pdev->dev, "Failed to request base memory region\n");
- goto err_clk_put;
+ goto err_cpufreq_unreg;
}
host->base = ioremap_nocache(host->mem->start, resource_size(host->mem));
@@ -929,6 +991,8 @@ err_iounmap:
iounmap(host->base);
err_release_mem_region:
release_mem_region(host->mem->start, resource_size(host->mem));
+err_cpufreq_unreg:
+ jz4740_mmc_cpufreq_unregister();
err_clk_put:
clk_put(host->clk);
err_free_host:
@@ -958,6 +1022,7 @@ static int __devexit jz4740_mmc_remove(struct platform_device *pdev)
iounmap(host->base);
release_mem_region(host->mem->start, resource_size(host->mem));
+ jz4740_mmc_cpufreq_unregister();
clk_put(host->clk);
platform_set_drvdata(pdev, NULL);
--
1.7.5.4

@ -0,0 +1,95 @@
From 2dcb0ca66d0bffc23d5f001fad81fb1a7a2c371b Mon Sep 17 00:00:00 2001
From: Maarten ter Huurne <maarten@treewalker.org>
Date: Tue, 28 Jun 2011 22:28:59 +0200
Subject: [PATCH 14/21] MIPS: JZ4740: reset: Initialize hibernate wakeup
counters.
In hibernation mode only the wakeup logic and the RTC are left running,
so this is what users perceive as power down.
If the counters are not initialized, the corresponding pin (typically
connected to the power button) has to be asserted for two seconds
before the device wakes up. Most users expect a shorter wakeup time.
I took the timing values of 100 ms and 60 ms from BouKiCHi's patch for
the Dingoo A320 kernel.
---
arch/mips/jz4740/reset.c | 46 ++++++++++++++++++++++++++++++++++++++++------
1 files changed, 40 insertions(+), 6 deletions(-)
diff --git a/arch/mips/jz4740/reset.c b/arch/mips/jz4740/reset.c
index 5f1fb95..e6d1d7b 100644
--- a/arch/mips/jz4740/reset.c
+++ b/arch/mips/jz4740/reset.c
@@ -21,6 +21,9 @@
#include <asm/mach-jz4740/base.h>
#include <asm/mach-jz4740/timer.h>
+#include "reset.h"
+#include "clock.h"
+
static void jz4740_halt(void)
{
while (1) {
@@ -53,21 +56,52 @@ static void jz4740_restart(char *command)
jz4740_halt();
}
-#define JZ_REG_RTC_CTRL 0x00
-#define JZ_REG_RTC_HIBERNATE 0x20
+#define JZ_REG_RTC_CTRL 0x00
+#define JZ_REG_RTC_HIBERNATE 0x20
+#define JZ_REG_RTC_WAKEUP_FILTER 0x24
+#define JZ_REG_RTC_RESET_COUNTER 0x28
-#define JZ_RTC_CTRL_WRDY BIT(7)
+#define JZ_RTC_CTRL_WRDY BIT(7)
+#define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0
+#define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0
-static void jz4740_power_off(void)
+static inline void jz4740_rtc_wait_ready(void __iomem *rtc_base)
{
- void __iomem *rtc_base = ioremap(JZ4740_RTC_BASE_ADDR, 0x24);
uint32_t ctrl;
-
do {
ctrl = readl(rtc_base + JZ_REG_RTC_CTRL);
} while (!(ctrl & JZ_RTC_CTRL_WRDY));
+}
+static void jz4740_power_off(void)
+{
+ void __iomem *rtc_base = ioremap(JZ4740_RTC_BASE_ADDR, 0x38);
+ unsigned long long wakeup_filter_ticks;
+ unsigned long long reset_counter_ticks;
+
+ /* Set minimum wakeup pin assertion time: 100 ms.
+ Range is 0 to 2 sec if RTC is clocked at 32 kHz. */
+ wakeup_filter_ticks = (100 * jz4740_clock_bdata.rtc_rate) / 1000;
+ if (wakeup_filter_ticks < JZ_RTC_WAKEUP_FILTER_MASK)
+ wakeup_filter_ticks &= JZ_RTC_WAKEUP_FILTER_MASK;
+ else
+ wakeup_filter_ticks = JZ_RTC_WAKEUP_FILTER_MASK;
+ jz4740_rtc_wait_ready(rtc_base);
+ writel(wakeup_filter_ticks, rtc_base + JZ_REG_RTC_WAKEUP_FILTER);
+
+ /* Set reset pin low-level assertion time after wakeup: 60 ms.
+ Range is 0 to 125 ms if RTC is clocked at 32 kHz. */
+ reset_counter_ticks = (60 * jz4740_clock_bdata.rtc_rate) / 1000;
+ if (reset_counter_ticks < JZ_RTC_RESET_COUNTER_MASK)
+ reset_counter_ticks &= JZ_RTC_RESET_COUNTER_MASK;
+ else
+ reset_counter_ticks = JZ_RTC_RESET_COUNTER_MASK;
+ jz4740_rtc_wait_ready(rtc_base);
+ writel(reset_counter_ticks, rtc_base + JZ_REG_RTC_RESET_COUNTER);
+
+ jz4740_rtc_wait_ready(rtc_base);
writel(1, rtc_base + JZ_REG_RTC_HIBERNATE);
+
jz4740_halt();
}
--
1.7.5.4

@ -0,0 +1,36 @@
From 515f8006c03e1065bf98c9148a9ea787e2d120d3 Mon Sep 17 00:00:00 2001
From: Maarten ter Huurne <maarten@treewalker.org>
Date: Wed, 10 Aug 2011 00:20:16 +0200
Subject: [PATCH 15/21] ASoC: JZ4740: Replaced comma operators with
semicolons.
They were harmless but also unnecessary, probably a leftover from earlier code.
---
sound/soc/jz4740/jz4740-i2s.c | 4 ++--
1 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c
index cd22a54..d28b4cc 100644
--- a/sound/soc/jz4740/jz4740-i2s.c
+++ b/sound/soc/jz4740/jz4740-i2s.c
@@ -346,7 +346,7 @@ static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
/* Playback */
dma_config = &i2s->pcm_config_playback.dma_config;
- dma_config->src_width = JZ4740_DMA_WIDTH_32BIT,
+ dma_config->src_width = JZ4740_DMA_WIDTH_32BIT;
dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
dma_config->request_type = JZ4740_DMA_TYPE_AIC_TRANSMIT;
dma_config->flags = JZ4740_DMA_SRC_AUTOINC;
@@ -355,7 +355,7 @@ static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s)
/* Capture */
dma_config = &i2s->pcm_config_capture.dma_config;
- dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT,
+ dma_config->dst_width = JZ4740_DMA_WIDTH_32BIT;
dma_config->transfer_size = JZ4740_DMA_TRANSFER_SIZE_16BYTE;
dma_config->request_type = JZ4740_DMA_TYPE_AIC_RECEIVE;
dma_config->flags = JZ4740_DMA_DST_AUTOINC;
--
1.7.5.4

@ -0,0 +1,94 @@
From 8a5087fe59e31efb8641e704058328997c3c8ff1 Mon Sep 17 00:00:00 2001
From: Maarten ter Huurne <maarten@treewalker.org>
Date: Wed, 10 Aug 2011 00:25:11 +0200
Subject: [PATCH 16/21] ASoC: JZ4740: Support buffer size that is not a
multiple of period size.
This fixes glitches triggered by libao, which sets time-based intervals
instead of byte-based intervals like SDL does.
Thanks to Paul Cercueil for figuring out that the buffer size was causing
the glitches and to Lars Clausen for helping me write the fix.
---
sound/soc/jz4740/jz4740-pcm.c | 21 ++++++++++++++++++---
1 files changed, 18 insertions(+), 3 deletions(-)
diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c
index d1989cd..1f9a005 100644
--- a/sound/soc/jz4740/jz4740-pcm.c
+++ b/sound/soc/jz4740/jz4740-pcm.c
@@ -31,6 +31,7 @@
struct jz4740_runtime_data {
unsigned long dma_period;
+ unsigned long dma_period_left;
dma_addr_t dma_start;
dma_addr_t dma_pos;
dma_addr_t dma_end;
@@ -67,10 +68,13 @@ static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd,
if (prtd->dma_pos == prtd->dma_end)
prtd->dma_pos = prtd->dma_start;
- if (prtd->dma_pos + prtd->dma_period > prtd->dma_end)
+ if (prtd->dma_period_left == 0)
+ prtd->dma_period_left = prtd->dma_period;
+
+ if (prtd->dma_pos + prtd->dma_period_left > prtd->dma_end)
count = prtd->dma_end - prtd->dma_pos;
else
- count = prtd->dma_period;
+ count = prtd->dma_period_left;
jz4740_dma_disable(prtd->dma);
@@ -85,6 +89,7 @@ static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd,
jz4740_dma_set_transfer_count(prtd->dma, count);
prtd->dma_pos += count;
+ prtd->dma_period_left -= count;
jz4740_dma_enable(prtd->dma);
}
@@ -96,7 +101,8 @@ static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err,
struct snd_pcm_runtime *runtime = substream->runtime;
struct jz4740_runtime_data *prtd = runtime->private_data;
- snd_pcm_period_elapsed(substream);
+ if (prtd->dma_period_left == 0)
+ snd_pcm_period_elapsed(substream);
jz4740_pcm_start_transfer(prtd, substream);
}
@@ -133,6 +139,7 @@ static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream,
runtime->dma_bytes = params_buffer_bytes(params);
prtd->dma_period = params_period_bytes(params);
+ prtd->dma_period_left = 0;
prtd->dma_start = runtime->dma_addr;
prtd->dma_pos = prtd->dma_start;
prtd->dma_end = prtd->dma_start + runtime->dma_bytes;
@@ -160,6 +167,7 @@ static int jz4740_pcm_prepare(struct snd_pcm_substream *substream)
if (!prtd->dma)
return -EBUSY;
+ prtd->dma_period_left = 0;
prtd->dma_pos = prtd->dma_start;
return 0;
@@ -219,6 +227,13 @@ static int jz4740_pcm_open(struct snd_pcm_substream *substream)
if (prtd == NULL)
return -ENOMEM;
+ /* Force period and buffer size to be a multiple of the DMA transfer
+ * size, which is 16 bytes. */
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 16);
+ snd_pcm_hw_constraint_step(runtime, 0,
+ SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 16);
+
snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware);
runtime->private_data = prtd;
--
1.7.5.4

@ -0,0 +1,26 @@
From cdb15b56aa9517a8cdbd14724088227514622a1b Mon Sep 17 00:00:00 2001
From: Maarten ter Huurne <maarten@treewalker.org>
Date: Wed, 11 Jan 2012 22:36:03 +0100
Subject: [PATCH 17/21] MIPS: JZ4740: qi_lb60: Look for NAND chip in bank 1.
All NanoNotes have their NAND in bank 1.
Specifying the bank is required since commit e7ca5a665877a030.
---
arch/mips/jz4740/board-qi_lb60.c | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c
index b622941..f2d94c1 100644
--- a/arch/mips/jz4740/board-qi_lb60.c
+++ b/arch/mips/jz4740/board-qi_lb60.c
@@ -140,6 +140,7 @@ static void qi_lb60_nand_ident(struct platform_device *pdev,
static struct jz_nand_platform_data qi_lb60_nand_pdata = {
.ident_callback = qi_lb60_nand_ident,
.busy_gpio = 94,
+ .banks = { 1 },
};
/* Keyboard*/
--
1.7.5.4

@ -0,0 +1,128 @@
From 94f2b91d2a42854a8869d54a758bc88757d7f6d9 Mon Sep 17 00:00:00 2001
From: Axel Lin <axel.lin@gmail.com>
Date: Fri, 6 Jan 2012 11:30:10 +0800
Subject: [PATCH 18/21] ASoC: jz4740: Convert qi_lb60 to use
snd_soc_register_card()
Use snd_soc_register_card() instead of creating a "soc-audio" platform device.
Signed-off-by: Axel Lin <axel.lin@gmail.com>
---
arch/mips/jz4740/board-qi_lb60.c | 6 ++++
sound/soc/jz4740/qi_lb60.c | 56 +++++++++++++++++--------------------
2 files changed, 32 insertions(+), 30 deletions(-)
diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c
index f2d94c1..be14dfb 100644
--- a/arch/mips/jz4740/board-qi_lb60.c
+++ b/arch/mips/jz4740/board-qi_lb60.c
@@ -418,6 +418,11 @@ static struct platform_device qi_lb60_charger_device = {
},
};
+/* audio */
+static struct platform_device qi_lb60_audio_device = {
+ .name = "qi-lb60-audio",
+ .id = -1,
+};
static struct platform_device *jz_platform_devices[] __initdata = {
&jz4740_udc_device,
@@ -434,6 +439,7 @@ static struct platform_device *jz_platform_devices[] __initdata = {
&qi_lb60_gpio_keys,
&qi_lb60_pwm_beeper,
&qi_lb60_charger_device,
+ &qi_lb60_audio_device,
};
static void __init board_gpio_setup(void)
diff --git a/sound/soc/jz4740/qi_lb60.c b/sound/soc/jz4740/qi_lb60.c
index c5fc339..67ddbdd 100644
--- a/sound/soc/jz4740/qi_lb60.c
+++ b/sound/soc/jz4740/qi_lb60.c
@@ -90,56 +90,52 @@ static struct snd_soc_card qi_lb60 = {
.num_dapm_routes = ARRAY_SIZE(qi_lb60_routes),
};
-static struct platform_device *qi_lb60_snd_device;
-
static const struct gpio qi_lb60_gpios[] = {
{ QI_LB60_SND_GPIO, GPIOF_OUT_INIT_LOW, "SND" },
{ QI_LB60_AMP_GPIO, GPIOF_OUT_INIT_LOW, "AMP" },
};
-static int __init qi_lb60_init(void)
+static int __devinit qi_lb60_probe(struct platform_device *pdev)
{
+ struct snd_soc_card *card = &qi_lb60;
int ret;
- qi_lb60_snd_device = platform_device_alloc("soc-audio", -1);
-
- if (!qi_lb60_snd_device)
- return -ENOMEM;
-
ret = gpio_request_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
- if (ret) {
- pr_err("qi_lb60 snd: Failed to request gpios: %d\n", ret);
- goto err_device_put;
- }
+ if (ret)
+ return ret;
- platform_set_drvdata(qi_lb60_snd_device, &qi_lb60);
+ card->dev = &pdev->dev;
- ret = platform_device_add(qi_lb60_snd_device);
+ ret = snd_soc_register_card(card);
if (ret) {
- pr_err("qi_lb60 snd: Failed to add snd soc device: %d\n", ret);
- goto err_unset_pdata;
+ dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret);
+ gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
}
-
- return 0;
-
-err_unset_pdata:
- platform_set_drvdata(qi_lb60_snd_device, NULL);
-/*err_gpio_free_array:*/
- gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
-err_device_put:
- platform_device_put(qi_lb60_snd_device);
-
return ret;
}
-module_init(qi_lb60_init);
-static void __exit qi_lb60_exit(void)
+static int __devexit qi_lb60_remove(struct platform_device *pdev)
{
- platform_device_unregister(qi_lb60_snd_device);
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ snd_soc_unregister_card(card);
gpio_free_array(qi_lb60_gpios, ARRAY_SIZE(qi_lb60_gpios));
+ return 0;
}
-module_exit(qi_lb60_exit);
+
+static struct platform_driver qi_lb60_driver = {
+ .driver = {
+ .name = "qi-lb60-audio",
+ .owner = THIS_MODULE,
+ },
+ .probe = qi_lb60_probe,
+ .remove = __devexit_p(qi_lb60_remove),
+};
+
+module_platform_driver(qi_lb60_driver);
MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
MODULE_DESCRIPTION("ALSA SoC QI LB60 Audio support");
MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qi-lb60-audio");
--
1.7.5.4

@ -0,0 +1,43 @@
From 159c4d81899db05a57bc4a0d55083e484c3d8a43 Mon Sep 17 00:00:00 2001
From: Lars-Peter Clausen <lars@metafoo.de>
Date: Sat, 24 Apr 2010 12:23:28 +0200
Subject: [PATCH 19/21] Framebuffer notifier: Call notifier callbacks prior to
blanking the screen
---
drivers/video/fbmem.c | 8 ++++++--
1 files changed, 6 insertions(+), 2 deletions(-)
diff --git a/drivers/video/fbmem.c b/drivers/video/fbmem.c
index ad93629..e13e6bf 100644
--- a/drivers/video/fbmem.c
+++ b/drivers/video/fbmem.c
@@ -1032,12 +1032,12 @@ fb_set_var(struct fb_info *info, struct fb_var_screeninfo *var)
int
fb_blank(struct fb_info *info, int blank)
{
- int ret = -EINVAL;
+ int ret = 0;
if (blank > FB_BLANK_POWERDOWN)
blank = FB_BLANK_POWERDOWN;
- if (info->fbops->fb_blank)
+ if (info->fbops->fb_blank && blank == FB_BLANK_UNBLANK)
ret = info->fbops->fb_blank(blank, info);
if (!ret) {
@@ -1048,6 +1048,10 @@ fb_blank(struct fb_info *info, int blank)
fb_notifier_call_chain(FB_EVENT_BLANK, &event);
}
+ if (info->fbops->fb_blank && blank != FB_BLANK_UNBLANK)
+ ret = info->fbops->fb_blank(blank, info);
+
+
return ret;
}
--
1.7.5.4

@ -0,0 +1,31 @@
From 1d7ef3445db5aebf48c90b20e18630633623c750 Mon Sep 17 00:00:00 2001
From: Xiangfu Liu <xiangfu@macbook.(none)>
Date: Fri, 19 Aug 2011 15:46:52 +0800
Subject: [PATCH 20/21] qi_lb60: NAND: add data partition
---
arch/mips/jz4740/board-qi_lb60.c | 7 ++++++-
1 files changed, 6 insertions(+), 1 deletions(-)
diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c
index be14dfb..4e24b95 100644
--- a/arch/mips/jz4740/board-qi_lb60.c
+++ b/arch/mips/jz4740/board-qi_lb60.c
@@ -118,8 +118,13 @@ static struct mtd_partition qi_lb60_partitions_2gb[] = {
{
.name = "NAND ROOTFS partition",
.offset = 8 * 0x100000,
- .size = (504 + 512 + 1024) * 0x100000,
+ .size = 512 * 0x100000,
},
+ {
+ .name = "NAND DATA partition",
+ .offset = 520 * 0x100000,
+ .size = 1528 * 0x100000,
+ },
};
static void qi_lb60_nand_ident(struct platform_device *pdev,
--
1.7.5.4

@ -0,0 +1,25 @@
From a37ca6eb8cb52d620feff3323a3be4da24f5bee8 Mon Sep 17 00:00:00 2001
From: Xiangfu <xiangfu@openmobilefree.net>
Date: Fri, 16 Mar 2012 17:48:45 +0800
Subject: [PATCH 21/21] rtc: jz4740 fix hwclock give time out
---
drivers/rtc/rtc-jz4740.c | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)
diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c
index b647363..50c5df0 100644
--- a/drivers/rtc/rtc-jz4740.c
+++ b/drivers/rtc/rtc-jz4740.c
@@ -280,6 +280,8 @@ static int __devinit jz4740_rtc_probe(struct platform_device *pdev)
}
}
+ jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ_IRQ, 1);
+
return 0;
err_free_irq:
--
1.7.5.4

@ -1,17 +0,0 @@
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_FB_JZ4740=y
# CONFIG_FB_TMIO is not set
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_INPUT_PWM_BEEPER=y
CONFIG_JZ4740_QI_LB60=y
CONFIG_KEYBOARD_GPIO=y
CONFIG_KEYBOARD_MATRIX=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_ILI8960=y
CONFIG_LOGO=y
CONFIG_MFD_CORE=y
CONFIG_MFD_JZ4740_ADC=y
CONFIG_SPI=y
CONFIG_SPI_BITBANG=y
CONFIG_SPI_GPIO=y
CONFIG_SPI_MASTER=y

@ -1,17 +0,0 @@
CONFIG_BACKLIGHT_LCD_SUPPORT=y
CONFIG_FB_JZ4740=y
# CONFIG_FB_TMIO is not set
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_INPUT_PWM_BEEPER=y
CONFIG_JZ4740_QI_LB60=y
CONFIG_KEYBOARD_GPIO=y
CONFIG_KEYBOARD_MATRIX=y
CONFIG_LCD_CLASS_DEVICE=y
CONFIG_LCD_ILI8960=y
CONFIG_LOGO=y
CONFIG_MFD_CORE=y
CONFIG_MFD_JZ4740_ADC=y
CONFIG_SPI=y
CONFIG_SPI_BITBANG=y
CONFIG_SPI_GPIO=y
CONFIG_SPI_MASTER=y

@ -0,0 +1,94 @@
CONFIG_DEVMEM=y
CONFIG_FB_JZ4740=y
# CONFIG_FB_TMIO is not set
CONFIG_FRAMEBUFFER_CONSOLE=y
CONFIG_INPUT_PWM_BEEPER=y
CONFIG_JZ4740_QI_LB60=y
CONFIG_KEYBOARD_GPIO=y
CONFIG_KEYBOARD_MATRIX=y
CONFIG_LOGO=y
# CONFIG_LOGO_LINUX_MONO is not set
# CONFIG_LOGO_LINUX_VGA16 is not set
# CONFIG_LOGO_LINUX_CLUT224 is not set
# CONFIG_LOGO_OPENWRT_CLUT224 is not set
CONFIG_LOGO_NANONOTE_CLUT224=y
CONFIG_SPI=y
CONFIG_SPI_BITBANG=y
CONFIG_SPI_GPIO=y
CONFIG_SPI_MASTER=y
CONFIG_SOUND=y
CONFIG_SND=y
CONFIG_SND_TIMER=y
CONFIG_SND_PCM=y
CONFIG_SND_HWDEP=y
CONFIG_SND_SEQUENCER=y
CONFIG_SND_SEQ_DUMMY=y
CONFIG_SND_MIXER_OSS=y
CONFIG_SND_PCM_OSS=y
# CONFIG_SND_RAWMIDI_SEQ is not set
CONFIG_SND_SOC=y
CONFIG_SND_JZ4740_SOC=y
CONFIG_SND_JZ4740_SOC_QI_LB60=y
CONFIG_SND_JZ4740_SOC_I2S=y
CONFIG_SND_SOC_I2C_AND_SPI=y
CONFIG_SND_SOC_JZCODEC=y
CONFIG_REGMAP=y
CONFIG_REGMAP_I2C=y
CONFIG_REGMAP_SPI=y
# CONFIG_SND_VIRMIDI is not set
CONFIG_SOUND_OSS_CORE_PRECLAIM=y
# CONFIG_SND_SEQUENCER_OSS is not set
# CONFIG_IEEE802154 is not set
# CONFIG_IEEE802154_6LOWPAN is not set
# CONFIG_MAC802154 is not set
# CONFIG_MAC802154_DEBUG is not set
# CONFIG_IEEE802154_DRIVER_DEBUG is not set
# CONFIG_IEEE802154_DRIVER is not set
# CONFIG_IEEE802154_SERIAL is not set
# CONFIG_SPI_ATUSB is not set
# CONFIG_SPI_ATBEN is not set
# CONFIG_IEEE802154_AT86RF230 is not set
# CONFIG_IEEE802154_FAKEHARD is not set
# CONFIG_IEEE802154_FAKELB is not set
# CONFIG_IEEE802154_CC2420 is not set
# CONFIG_IEEE802154_ADF7242 is not set
# CONFIG_SENSORS_JZ4740 is not set
CONFIG_LCD_CLASS_DEVICE=y
# CONFIG_LCD_L4F00242T03 is not set
# CONFIG_LCD_LMS283GF05 is not set
# CONFIG_LCD_LTV350QV is not set
CONFIG_LCD_ILI8960=y
CONFIG_BACKLIGHT_LCD_SUPPORT=y
# CONFIG_BACKLIGHT_CLASS_DEVICE is not set
# CONFIG_BACKLIGHT_GENERIC is not set
# CONFIG_BACKLIGHT_PWM is not set
# CONFIG_BACKLIGHT_ADP8860 is not set
# CONFIG_BACKLIGHT_ADP8870 is not set
# CONFIG_LCD_TDO24M is not set
# CONFIG_LCD_VGG2432A4 is not set
# CONFIG_LCD_PLATFORM is not set
# CONFIG_USB_RENESAS_USBHS is not set
CONFIG_USB_GADGET=y
# CONFIG_USB_GADGET_DEBUG is not set
# CONFIG_USB_GADGET_DEBUG_FILES is not set
# CONFIG_USB_GADGET_DEBUG_FS is not set
CONFIG_USB_GADGET_VBUS_DRAW=2
CONFIG_USB_GADGET_STORAGE_NUM_BUFFERS=2
# CONFIG_USB_FUSB300 is not set
CONFIG_USB_JZ4740=y
# CONFIG_USB_R8A66597 is not set
# CONFIG_USB_M66592 is not set
# CONFIG_USB_NET2272 is not set
CONFIG_USB_GADGET_DUALSPEED=y
# CONFIG_USB_ZERO is not set
# CONFIG_USB_AUDIO is not set
CONFIG_USB_ETH=y
# CONFIG_USB_ETH_EEM is not set
# CONFIG_USB_ETH_RNDIS is not set

@ -1,8 +1 @@
BOARDNAME:=QI Ben Nanonote (qi_lb60)
DEFAULT_PACKAGES += \
kmod-sound-core \
kmod-sound-soc-core \
kmod-sound-soc-jz4740 \
kmod-sound-soc-jz4740-codec \
kmod-sound-soc-qilb60 \

Loading…
Cancel
Save