diff options
Diffstat (limited to 'target/linux/xburst')
17 files changed, 5427 insertions, 0 deletions
diff --git a/target/linux/xburst/config-3.10 b/target/linux/xburst/config-3.10 new file mode 100644 index 0000000..511e692 --- /dev/null +++ b/target/linux/xburst/config-3.10 @@ -0,0 +1,331 @@ +CONFIG_ARCH_BINFMT_ELF_RANDOMIZE_PIE=y +CONFIG_ARCH_DISCARD_MEMBLOCK=y +CONFIG_ARCH_HAS_ATOMIC64_DEC_IF_POSITIVE=y +CONFIG_ARCH_HAVE_CUSTOM_GPIO_H=y +CONFIG_ARCH_HIBERNATION_POSSIBLE=y +CONFIG_ARCH_REQUIRE_GPIOLIB=y +CONFIG_ARCH_SUSPEND_POSSIBLE=y +CONFIG_ARCH_WANT_IPC_PARSE_VERSION=y +# CONFIG_ARPD is not set +# CONFIG_BACKLIGHT_ADP8860 is not set +# CONFIG_BACKLIGHT_ADP8870 is not set +CONFIG_BACKLIGHT_CLASS_DEVICE=y +# CONFIG_BACKLIGHT_GENERIC is not set +CONFIG_BACKLIGHT_LCD_SUPPORT=y +# CONFIG_BACKLIGHT_PWM is not set +CONFIG_BATTERY_JZ4740=y +# CONFIG_BLK_DEV_INITRD is not set +# CONFIG_BRIDGE is not set +CONFIG_CHARGER_GPIO=y +CONFIG_CLONE_BACKWARDS=y +CONFIG_CONFIGFS_FS=y +CONFIG_CONSOLE_TRANSLATIONS=y +CONFIG_CPU_GENERIC_DUMP_TLB=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_R4K_CACHE_TLB=y +CONFIG_CPU_R4K_FPU=y +CONFIG_CPU_SUPPORTS_32BIT_KERNEL=y +CONFIG_CPU_SUPPORTS_HIGHMEM=y +CONFIG_CRC16=y +CONFIG_CRYPTO_CRC32C=y +CONFIG_CRYPTO_DEFLATE=y +CONFIG_CRYPTO_HASH=y +CONFIG_CRYPTO_HASH2=y +CONFIG_CRYPTO_LZO=y +CONFIG_CRYPTO_XZ=y +CONFIG_DMADEVICES=y +CONFIG_DMA_ENGINE=y +CONFIG_DMA_JZ4740=y +CONFIG_DMA_NONCOHERENT=y +CONFIG_DMA_VIRTUAL_CHANNELS=y +CONFIG_DUMMY_CONSOLE=y +CONFIG_EARLY_PRINTK=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_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_IO=y +CONFIG_GENERIC_IRQ_CHIP=y +CONFIG_GENERIC_IRQ_SHOW=y +CONFIG_GENERIC_SMP_IDLE_THREAD=y +CONFIG_GPIOLIB=y +CONFIG_GPIO_DEVRES=y +# CONFIG_HAMRADIO is not set +CONFIG_HARDWARE_WATCHPOINTS=y +CONFIG_HAS_DMA=y +CONFIG_HAS_IOMEM=y +CONFIG_HAS_IOPORT=y +# CONFIG_HAVE_64BIT_ALIGNED_ACCESS is not set +CONFIG_HAVE_ARCH_JUMP_LABEL=y +CONFIG_HAVE_ARCH_KGDB=y +# CONFIG_HAVE_BOOTMEM_INFO_NODE is not set +CONFIG_HAVE_CLK=y +CONFIG_HAVE_C_RECORDMCOUNT=y +CONFIG_HAVE_DEBUG_KMEMLEAK=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_KERNEL_BZIP2=y +CONFIG_HAVE_KERNEL_GZIP=y +CONFIG_HAVE_KERNEL_LZMA=y +CONFIG_HAVE_KERNEL_LZO=y +CONFIG_HAVE_MEMBLOCK=y +CONFIG_HAVE_MEMBLOCK_NODE_MAP=y +CONFIG_HAVE_MOD_ARCH_SPECIFIC=y +CONFIG_HAVE_NET_DSA=y +CONFIG_HAVE_OPROFILE=y +CONFIG_HAVE_PERF_EVENTS=y +# CONFIG_HIGH_RES_TIMERS is not set +CONFIG_HW_CONSOLE=y +CONFIG_HZ_PERIODIC=y +CONFIG_I2C=y +CONFIG_I2C_BOARDINFO=y +CONFIG_INPUT=y +CONFIG_INPUT_EVDEV=y +CONFIG_INPUT_KEYBOARD=y +CONFIG_INPUT_MATRIXKMAP=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=y +CONFIG_INPUT_UINPUT=y +CONFIG_IRQ_CPU=y +CONFIG_IRQ_DOMAIN=y +CONFIG_IRQ_FORCED_THREADING=y +CONFIG_IRQ_WORK=y +CONFIG_JBD2=y +CONFIG_JZ4740_QI_LB60=y +CONFIG_KALLSYMS=y +CONFIG_KERNEL_GZIP=y +CONFIG_KEXEC=y +CONFIG_KEYBOARD_GPIO=y +CONFIG_KEYBOARD_MATRIX=y +# CONFIG_LCD_AMS369FG06 is not set +CONFIG_LCD_CLASS_DEVICE=y +CONFIG_LCD_ILI8960=y +# CONFIG_LCD_L4F00242T03 is not set +# CONFIG_LCD_LD9040 is not set +# CONFIG_LCD_LMS283GF05 is not set +# CONFIG_LCD_LTV350QV is not set +# CONFIG_LCD_PLATFORM is not set +# CONFIG_LCD_S6E63M0 is not set +# CONFIG_LCD_TDO24M is not set +# CONFIG_LCD_VGG2432A4 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_MIPS=y +CONFIG_MIPS_FPU_EMU=y +# CONFIG_MIPS_HUGE_TLB_SUPPORT is not set +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_MODULES_USE_ELF_REL=y +# CONFIG_MOUSE_BCM5974 is not set +# CONFIG_MOUSE_CYAPA is not set +# CONFIG_MOUSE_PS2 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_LIMIT=20 +# CONFIG_MTD_UBI_FASTMAP 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_DMA 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_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_PWM=y +CONFIG_PWM_JZ4740=y +# CONFIG_RCU_BOOST is not set +CONFIG_RCU_CPU_STALL_VERBOSE=y +CONFIG_RCU_STALL_COMMON=y +CONFIG_REGMAP=y +CONFIG_REGMAP_I2C=y +CONFIG_REGMAP_MMIO=y +CONFIG_REGMAP_SPI=y +CONFIG_RTC_CLASS=y +# CONFIG_RTC_DRV_CMOS is not set +CONFIG_RTC_DRV_JZ4740=y +# CONFIG_SCSI_DMA is not set +CONFIG_SERIO=y +CONFIG_SERIO_LIBPS2=y +CONFIG_SND=y +CONFIG_SND_COMPRESS_OFFLOAD=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_DMAENGINE_PCM=y +CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM=y +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_SPI=y +CONFIG_SPI_BITBANG=y +CONFIG_SPI_GPIO=y +CONFIG_SPI_MASTER=y +# 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_SYS_SUPPORTS_ZBOOT=y +CONFIG_SYS_SUPPORTS_ZBOOT_UART16550=y +CONFIG_TICK_CPU_ACCOUNTING=y +# CONFIG_TINY_PREEMPT_RCU is not set +CONFIG_TREE_PREEMPT_RCU=y +CONFIG_UBIFS_FS=y +CONFIG_UBIFS_FS_ADVANCED_COMPR=y +CONFIG_UBIFS_FS_LZO=y +CONFIG_UBIFS_FS_XZ=y +CONFIG_UBIFS_FS_ZLIB=y +CONFIG_UIDGID_CONVERTED=y +CONFIG_UNINLINE_SPIN_UNLOCK=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_COMMON=y +CONFIG_USB_ETH=y +# CONFIG_USB_ETH_EEM is not set +# CONFIG_USB_ETH_RNDIS is not set +CONFIG_USB_GADGET=y +CONFIG_USB_JZ4740=y +CONFIG_USB_LIBCOMPOSITE=y +# CONFIG_USB_MV_U3D is not set +# CONFIG_USB_PXA27X is not set +CONFIG_USB_SUPPORT=y +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_ZLIB_DEFLATE=y +CONFIG_ZLIB_INFLATE=y +CONFIG_ZONE_DMA_FLAG=0 diff --git a/target/linux/xburst/patches-3.10/001-ubi-Read-only-the-vid-header-instead-of-the-whole-pa.patch b/target/linux/xburst/patches-3.10/001-ubi-Read-only-the-vid-header-instead-of-the-whole-pa.patch new file mode 100644 index 0000000..15d90be --- /dev/null +++ b/target/linux/xburst/patches-3.10/001-ubi-Read-only-the-vid-header-instead-of-the-whole-pa.patch @@ -0,0 +1,27 @@ +From 10fd63ee59dd9de4174f92f2f292b82fe46f1546 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/16] ubi: Read only the vid header instead of the whole + page + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +--- + drivers/mtd/ubi/io.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c +index bf79def..bc82962 100644 +--- a/drivers/mtd/ubi/io.c ++++ b/drivers/mtd/ubi/io.c +@@ -1019,7 +1019,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.10.4 + diff --git a/target/linux/xburst/patches-3.10/002-NAND-Optimize-NAND_ECC_HW_OOB_FIRST-read.patch b/target/linux/xburst/patches-3.10/002-NAND-Optimize-NAND_ECC_HW_OOB_FIRST-read.patch new file mode 100644 index 0000000..da951dc --- /dev/null +++ b/target/linux/xburst/patches-3.10/002-NAND-Optimize-NAND_ECC_HW_OOB_FIRST-read.patch @@ -0,0 +1,50 @@ +From f2b7099d3986e59aab5792cecc052e440caf2cdd 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 02/16] NAND: Optimize NAND_ECC_HW_OOB_FIRST read + +Avoid sending unnecessary READ commands to the chip. + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +--- + drivers/mtd/nand/nand_base.c | 17 +++++++++++++---- + 1 file changed, 13 insertions(+), 4 deletions(-) + +diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c +index dfcd0a5..287c433 100644 +--- a/drivers/mtd/nand/nand_base.c ++++ b/drivers/mtd/nand/nand_base.c +@@ -1278,9 +1278,16 @@ static int nand_read_page_hwecc_oob_first(struct mtd_info *mtd, + unsigned int max_bitflips = 0; + + /* 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); ++ /* 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, 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]]; +@@ -1455,7 +1462,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, + if (realpage != chip->pagebuf || oob) { + bufpoi = aligned ? buf : chip->buffers->databuf; + +- chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); ++ if (chip->ecc.mode != NAND_ECC_HW_OOB_FIRST) { ++ chip->cmdfunc(mtd, NAND_CMD_READ0, 0x00, page); ++ } + + /* + * Now read the page into the buffer. Absent an error, +-- +1.7.10.4 + diff --git a/target/linux/xburst/patches-3.10/003-NAND-Add-support-for-subpage-reads-for-NAND_ECC_HW_O.patch b/target/linux/xburst/patches-3.10/003-NAND-Add-support-for-subpage-reads-for-NAND_ECC_HW_O.patch new file mode 100644 index 0000000..2e67ad9 --- /dev/null +++ b/target/linux/xburst/patches-3.10/003-NAND-Add-support-for-subpage-reads-for-NAND_ECC_HW_O.patch @@ -0,0 +1,141 @@ +From 90e325c5e16db262818bca442b00f5ac10b9c852 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 03/16] NAND: Add support for subpage reads for + NAND_ECC_HW_OOB_FIRST + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +--- + drivers/mtd/nand/nand_base.c | 80 ++++++++++++++++++++++++++++++++++++++++-- + include/linux/mtd/nand.h | 2 +- + 2 files changed, 78 insertions(+), 4 deletions(-) + +diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c +index 287c433..0fd6f2e 100644 +--- a/drivers/mtd/nand/nand_base.c ++++ b/drivers/mtd/nand/nand_base.c +@@ -1118,7 +1118,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; +@@ -1311,6 +1311,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 +@@ -1477,7 +1546,7 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, + else if (!aligned && NAND_HAS_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, + oob_required, page); +@@ -3475,8 +3544,13 @@ 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; ++ chip->options |= NAND_SUBPAGE_READ; ++ } ++ } + + 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 ab63634..ff5b62e 100644 +--- a/include/linux/mtd/nand.h ++++ b/include/linux/mtd/nand.h +@@ -349,7 +349,7 @@ struct nand_ecc_ctrl { + int (*read_page)(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int oob_required, 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); + int (*write_subpage)(struct mtd_info *mtd, struct nand_chip *chip, + uint32_t offset, uint32_t data_len, + const uint8_t *data_buf, int oob_required); +-- +1.7.10.4 + diff --git a/target/linux/xburst/patches-3.10/004-MMC-JZ4740-Remove-duplicated-code.patch b/target/linux/xburst/patches-3.10/004-MMC-JZ4740-Remove-duplicated-code.patch new file mode 100644 index 0000000..7a8ab65 --- /dev/null +++ b/target/linux/xburst/patches-3.10/004-MMC-JZ4740-Remove-duplicated-code.patch @@ -0,0 +1,29 @@ +From 3e46e1ea552c3cc5fb64dfeab22124a0e8b94495 Mon Sep 17 00:00:00 2001 +From: Paul Cercueil <paul@crapouillou.net> +Date: Sun, 3 Jun 2012 12:45:23 +0200 +Subject: [PATCH 04/16] MMC: JZ4740: Remove duplicated code. + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +--- + drivers/mmc/host/jz4740_mmc.c | 5 ----- + 1 file changed, 5 deletions(-) + +diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c +index 2391c6b..5a6ac99 100644 +--- a/drivers/mmc/host/jz4740_mmc.c ++++ b/drivers/mmc/host/jz4740_mmc.c +@@ -560,11 +560,6 @@ static irqreturn_t jz_mmc_irq(int irq, void *devid) + if (cmd->data) + cmd->data->error = -EIO; + cmd->error = -EIO; +- } else if (status & (JZ_MMC_STATUS_CRC_READ_ERROR | +- JZ_MMC_STATUS_CRC_WRITE_ERROR)) { +- if (cmd->data) +- cmd->data->error = -EIO; +- cmd->error = -EIO; + } + + jz4740_mmc_set_irq_enabled(host, irq_reg, false); +-- +1.7.10.4 + diff --git a/target/linux/xburst/patches-3.10/005-MMC-JZ4740-Fix-handling-of-read-errors.patch b/target/linux/xburst/patches-3.10/005-MMC-JZ4740-Fix-handling-of-read-errors.patch new file mode 100644 index 0000000..aef88f0 --- /dev/null +++ b/target/linux/xburst/patches-3.10/005-MMC-JZ4740-Fix-handling-of-read-errors.patch @@ -0,0 +1,35 @@ +From 81d112d6c0e16fd0136ce2c5f511ba81ed1f23d2 Mon Sep 17 00:00:00 2001 +From: Paul Cercueil <paul@crapouillou.net> +Date: Sun, 3 Jun 2012 13:07:19 +0200 +Subject: [PATCH 05/16] MMC: JZ4740: Fix handling of read errors. + +For no reason, the code handling write errors was implemented while +the code handling read errors was missing. + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +--- + drivers/mmc/host/jz4740_mmc.c | 8 ++++++++ + 1 file changed, 8 insertions(+) + +diff --git a/drivers/mmc/host/jz4740_mmc.c b/drivers/mmc/host/jz4740_mmc.c +index 5a6ac99..ffe1181 100644 +--- a/drivers/mmc/host/jz4740_mmc.c ++++ b/drivers/mmc/host/jz4740_mmc.c +@@ -231,6 +231,14 @@ static void jz4740_mmc_transfer_check_state(struct jz4740_mmc_host *host, + host->req->cmd->error = -EIO; + data->error = -EIO; + } ++ } else if (status & JZ_MMC_STATUS_READ_ERROR_MASK) { ++ if (status & (JZ_MMC_STATUS_TIMEOUT_READ)) { ++ host->req->cmd->error = -ETIMEDOUT; ++ data->error = -ETIMEDOUT; ++ } else { ++ host->req->cmd->error = -EIO; ++ data->error = -EIO; ++ } + } + } + +-- +1.7.10.4 + diff --git a/target/linux/xburst/patches-3.10/006-ASoC-JZ4740-delay-activation-of-the-DAC-to-work-arou.patch b/target/linux/xburst/patches-3.10/006-ASoC-JZ4740-delay-activation-of-the-DAC-to-work-arou.patch new file mode 100644 index 0000000..b4b69a8 --- /dev/null +++ b/target/linux/xburst/patches-3.10/006-ASoC-JZ4740-delay-activation-of-the-DAC-to-work-arou.patch @@ -0,0 +1,40 @@ +From 46c628ae74578382ec324405dd4caafdbf3838bd Mon Sep 17 00:00:00 2001 +From: Paul Cercueil <paul@crapouillou.net> +Date: Sat, 16 Jun 2012 19:36:31 +0200 +Subject: [PATCH 06/16] ASoC: JZ4740: delay activation of the DAC to work + around a sound bug. + +A proper fix of that bug would require a big rewrite of the driver, +which (I hope) will be done eventually. + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +--- + sound/soc/codecs/jz4740.c | 9 ++++++--- + 1 file changed, 6 insertions(+), 3 deletions(-) + +diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c +index 5f607b3..76ff580 100644 +--- a/sound/soc/codecs/jz4740.c ++++ b/sound/soc/codecs/jz4740.c +@@ -249,12 +249,15 @@ static int jz4740_codec_set_bias_level(struct snd_soc_codec *codec, + case SND_SOC_BIAS_ON: + break; + case SND_SOC_BIAS_PREPARE: +- mask = JZ4740_CODEC_1_VREF_DISABLE | +- JZ4740_CODEC_1_VREF_AMP_DISABLE | +- JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; ++ mask = JZ4740_CODEC_1_HEADPHONE_POWERDOWN_M; + value = 0; + + regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, value); ++ ++ msleep(500); ++ mask = JZ4740_CODEC_1_VREF_DISABLE | ++ JZ4740_CODEC_1_VREF_AMP_DISABLE; ++ regmap_update_bits(regmap, JZ4740_REG_CODEC_1, mask, 0); + break; + case SND_SOC_BIAS_STANDBY: + /* The only way to clear the suspend flag is to reset the codec */ +-- +1.7.10.4 + diff --git a/target/linux/xburst/patches-3.10/007-RTC-JZ4740-Init-the-regulator-register-on-startup.patch b/target/linux/xburst/patches-3.10/007-RTC-JZ4740-Init-the-regulator-register-on-startup.patch new file mode 100644 index 0000000..8dfae3a --- /dev/null +++ b/target/linux/xburst/patches-3.10/007-RTC-JZ4740-Init-the-regulator-register-on-startup.patch @@ -0,0 +1,61 @@ +From aff616f4a33bd3a9ab1506fdbe97fcfe285cb7b0 Mon Sep 17 00:00:00 2001 +From: Paul Cercueil <paul@crapouillou.net> +Date: Thu, 13 Sep 2012 00:09:20 +0200 +Subject: [PATCH 07/16] RTC: JZ4740: Init the "regulator" register on startup. + +This register controls the accuracy of the RTC. uC/OS-II use +the RTC as a 100Hz clock, and writes a completely wrong value +on that register, that we have to overwrite if we want a working +real-time clock. + +Signed-off-by: Paul Cercueil <paul@crapouillou.net> +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +--- + drivers/rtc/rtc-jz4740.c | 17 +++++++++++++++++ + 1 file changed, 17 insertions(+) + +diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c +index 1e48686..722fb0c 100644 +--- a/drivers/rtc/rtc-jz4740.c ++++ b/drivers/rtc/rtc-jz4740.c +@@ -14,6 +14,7 @@ + * + */ + ++#include <linux/clk.h> + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/platform_device.h> +@@ -215,6 +216,7 @@ static int jz4740_rtc_probe(struct platform_device *pdev) + int ret; + struct jz4740_rtc *rtc; + uint32_t scratchpad; ++ struct clk *rtc_clk; + + rtc = kzalloc(sizeof(*rtc), GFP_KERNEL); + if (!rtc) +@@ -280,6 +282,21 @@ static int jz4740_rtc_probe(struct platform_device *pdev) + } + } + ++ rtc_clk = clk_get(&pdev->dev, "rtc"); ++ if (IS_ERR(rtc_clk)) { ++ dev_err(&pdev->dev, "Failed to get RTC clock\n"); ++ goto err_free_irq; ++ } ++ ++ /* TODO: initialize the ADJC bits (25:16) to fine-tune ++ * the accuracy of the RTC */ ++ ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_REGULATOR, ++ (clk_get_rate(rtc_clk) - 1) & 0xffff); ++ clk_put(rtc_clk); ++ ++ if (ret) ++ dev_warn(&pdev->dev, "Could not update RTC regulator register\n"); ++ + return 0; + + err_free_irq: +-- +1.7.10.4 + diff --git a/target/linux/xburst/patches-3.10/008-Add-jz4740-udc-driver.patch b/target/linux/xburst/patches-3.10/008-Add-jz4740-udc-driver.patch new file mode 100644 index 0000000..6fc025b --- /dev/null +++ b/target/linux/xburst/patches-3.10/008-Add-jz4740-udc-driver.patch @@ -0,0 +1,2330 @@ +From b3e08e29f6f32dfb400374dc96d0a2f61e6adceb Mon Sep 17 00:00:00 2001 +From: Lars-Peter Clausen <lars@metafoo.de> +Date: Sat, 24 Apr 2010 12:18:46 +0200 +Subject: [PATCH 08/16] Add jz4740 udc driver + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +--- + drivers/usb/gadget/Kconfig | 8 + + drivers/usb/gadget/Makefile | 1 + + drivers/usb/gadget/gadget_chips.h | 1 + + drivers/usb/gadget/jz4740_udc.c | 2155 +++++++++++++++++++++++++++++++++++++ + drivers/usb/gadget/jz4740_udc.h | 101 ++ + 5 files changed, 2266 insertions(+) + create mode 100644 drivers/usb/gadget/jz4740_udc.c + create mode 100644 drivers/usb/gadget/jz4740_udc.h + +diff --git a/drivers/usb/gadget/Kconfig b/drivers/usb/gadget/Kconfig +index f41aa0d..c96b7ef 100644 +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -192,6 +192,14 @@ config USB_FUSB300 + help + Faraday usb device controller FUSB300 driver + ++config USB_JZ4740 ++ tristate "JZ4740 UDC" ++ depends on MACH_JZ4740 ++ select USB_GADGET_DUALSPEED ++ help ++ Select this to support the Ingenic JZ4740 processor ++ high speed USB device controller. ++ + config USB_OMAP + tristate "OMAP USB Device Controller" + depends on ARCH_OMAP1 +diff --git a/drivers/usb/gadget/Makefile b/drivers/usb/gadget/Makefile +index 6afd166..f18db69 100644 +--- a/drivers/usb/gadget/Makefile ++++ b/drivers/usb/gadget/Makefile +@@ -34,6 +34,7 @@ obj-$(CONFIG_USB_MV_UDC) += mv_udc.o + mv_udc-y := mv_udc_core.o + obj-$(CONFIG_USB_FUSB300) += fusb300_udc.o + obj-$(CONFIG_USB_MV_U3D) += mv_u3d_core.o ++obj-$(CONFIG_USB_JZ4740) += jz4740_udc.o + + # USB Functions + usb_f_acm-y := f_acm.o +diff --git a/drivers/usb/gadget/gadget_chips.h b/drivers/usb/gadget/gadget_chips.h +index bcd04bc..a3b069f 100644 +--- a/drivers/usb/gadget/gadget_chips.h ++++ b/drivers/usb/gadget/gadget_chips.h +@@ -29,6 +29,7 @@ + */ + #define gadget_is_at91(g) (!strcmp("at91_udc", (g)->name)) + #define gadget_is_goku(g) (!strcmp("goku_udc", (g)->name)) ++#define gadget_is_jz4740(g) (!strcmp("ingenic_hsusb", (g)->name)) + #define gadget_is_musbhdrc(g) (!strcmp("musb-hdrc", (g)->name)) + #define gadget_is_net2280(g) (!strcmp("net2280", (g)->name)) + #define gadget_is_pxa(g) (!strcmp("pxa25x_udc", (g)->name)) +diff --git a/drivers/usb/gadget/jz4740_udc.c b/drivers/usb/gadget/jz4740_udc.c +new file mode 100644 +index 0000000..72e9a6c +--- /dev/null ++++ b/drivers/usb/gadget/jz4740_udc.c +@@ -0,0 +1,2155 @@ ++/* ++ * linux/drivers/usb/gadget/jz4740_udc.c ++ * ++ * Ingenic JZ4740 on-chip high speed USB device controller ++ * ++ * Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc. ++ * Author: <jlwei@ingenic.cn> ++ * ++ * 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. ++ */ ++ ++/* ++ * This device has ep0, two bulk-in/interrupt-in endpoints, and one bulk-out endpoint. ++ * ++ * - Endpoint numbering is fixed: ep0, ep1in-int, ep2in-bulk, ep1out-bulk. ++ * - DMA works with bulk-in (channel 1) and bulk-out (channel 2) endpoints. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/delay.h> ++#include <linux/ioport.h> ++#include <linux/slab.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/proc_fs.h> ++#include <linux/usb.h> ++#include <linux/usb/gadget.h> ++#include <linux/clk.h> ++ ++#include <asm/byteorder.h> ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/mach-jz4740/clock.h> ++ ++#include "jz4740_udc.h" ++ ++#define JZ_REG_UDC_FADDR 0x00 /* Function Address 8-bit */ ++#define JZ_REG_UDC_POWER 0x01 /* Power Management 8-bit */ ++#define JZ_REG_UDC_INTRIN 0x02 /* Interrupt IN 16-bit */ ++#define JZ_REG_UDC_INTROUT 0x04 /* Interrupt OUT 16-bit */ ++#define JZ_REG_UDC_INTRINE 0x06 /* Intr IN enable 16-bit */ ++#define JZ_REG_UDC_INTROUTE 0x08 /* Intr OUT enable 16-bit */ ++#define JZ_REG_UDC_INTRUSB 0x0a /* Interrupt USB 8-bit */ ++#define JZ_REG_UDC_INTRUSBE 0x0b /* Interrupt USB Enable 8-bit */ ++#define JZ_REG_UDC_FRAME 0x0c /* Frame number 16-bit */ ++#define JZ_REG_UDC_INDEX 0x0e /* Index register 8-bit */ ++#define JZ_REG_UDC_TESTMODE 0x0f /* USB test mode 8-bit */ ++ ++#define JZ_REG_UDC_CSR0 0x12 /* EP0 CSR 8-bit */ ++#define JZ_REG_UDC_INMAXP 0x10 /* EP1-2 IN Max Pkt Size 16-bit */ ++#define JZ_REG_UDC_INCSR 0x12 /* EP1-2 IN CSR LSB 8/16bit */ ++#define JZ_REG_UDC_INCSRH 0x13 /* EP1-2 IN CSR MSB 8-bit */ ++ ++#define JZ_REG_UDC_OUTMAXP 0x14 /* EP1 OUT Max Pkt Size 16-bit */ ++#define JZ_REG_UDC_OUTCSR 0x16 /* EP1 OUT CSR LSB 8/16bit */ ++#define JZ_REG_UDC_OUTCSRH 0x17 /* EP1 OUT CSR MSB 8-bit */ ++#define JZ_REG_UDC_OUTCOUNT 0x18 /* bytes in EP0/1 OUT FIFO 16-bit */ ++ ++#define JZ_REG_UDC_EP_FIFO(x) (4 * (x) + 0x20) ++ ++#define JZ_REG_UDC_EPINFO 0x78 /* Endpoint information */ ++#define JZ_REG_UDC_RAMINFO 0x79 /* RAM information */ ++ ++#define JZ_REG_UDC_INTR 0x200 /* DMA pending interrupts */ ++#define JZ_REG_UDC_CNTL1 0x204 /* DMA channel 1 control */ ++#define JZ_REG_UDC_ADDR1 0x208 /* DMA channel 1 AHB memory addr */ ++#define JZ_REG_UDC_COUNT1 0x20c /* DMA channel 1 byte count */ ++#define JZ_REG_UDC_CNTL2 0x214 /* DMA channel 2 control */ ++#define JZ_REG_UDC_ADDR2 0x218 /* DMA channel 2 AHB memory addr */ ++#define JZ_REG_UDC_COUNT2 0x21c /* DMA channel 2 byte count */ ++ ++/* Power register bit masks */ ++#define USB_POWER_SUSPENDM 0x01 ++#define USB_POWER_RESUME 0x04 ++#define USB_POWER_HSMODE 0x10 ++#define USB_POWER_HSENAB 0x20 ++#define USB_POWER_SOFTCONN 0x40 ++ ++/* Interrupt register bit masks */ ++#define USB_INTR_SUSPEND 0x01 ++#define USB_INTR_RESUME 0x02 ++#define USB_INTR_RESET 0x04 ++ ++#define USB_INTR_EP0 0x0001 ++#define USB_INTR_INEP1 0x0002 ++#define USB_INTR_INEP2 0x0004 ++#define USB_INTR_OUTEP1 0x0002 ++ ++/* CSR0 bit masks */ ++#define USB_CSR0_OUTPKTRDY 0x01 ++#define USB_CSR0_INPKTRDY 0x02 ++#define USB_CSR0_SENTSTALL 0x04 ++#define USB_CSR0_DATAEND 0x08 ++#define USB_CSR0_SETUPEND 0x10 ++#define USB_CSR0_SENDSTALL 0x20 ++#define USB_CSR0_SVDOUTPKTRDY 0x40 ++#define USB_CSR0_SVDSETUPEND 0x80 ++ ++/* Endpoint CSR register bits */ ++#define USB_INCSRH_AUTOSET 0x80 ++#define USB_INCSRH_ISO 0x40 ++#define USB_INCSRH_MODE 0x20 ++#define USB_INCSRH_DMAREQENAB 0x10 ++#define USB_INCSRH_DMAREQMODE 0x04 ++#define USB_INCSR_CDT 0x40 ++#define USB_INCSR_SENTSTALL 0x20 ++#define USB_INCSR_SENDSTALL 0x10 ++#define USB_INCSR_FF 0x08 ++#define USB_INCSR_UNDERRUN 0x04 ++#define USB_INCSR_FFNOTEMPT 0x02 ++#define USB_INCSR_INPKTRDY 0x01 ++ ++#define USB_OUTCSRH_AUTOCLR 0x80 ++#define USB_OUTCSRH_ISO 0x40 ++#define USB_OUTCSRH_DMAREQENAB 0x20 ++#define USB_OUTCSRH_DNYT 0x10 ++#define USB_OUTCSRH_DMAREQMODE 0x08 ++#define USB_OUTCSR_CDT 0x80 ++#define USB_OUTCSR_SENTSTALL 0x40 ++#define USB_OUTCSR_SENDSTALL 0x20 ++#define USB_OUTCSR_FF 0x10 ++#define USB_OUTCSR_DATAERR 0x08 ++#define USB_OUTCSR_OVERRUN 0x04 ++#define USB_OUTCSR_FFFULL 0x02 ++#define USB_OUTCSR_OUTPKTRDY 0x01 ++ ++/* DMA control bits */ ++#define USB_CNTL_ENA 0x01 ++#define USB_CNTL_DIR_IN 0x02 ++#define USB_CNTL_MODE_1 0x04 ++#define USB_CNTL_INTR_EN 0x08 ++#define USB_CNTL_EP(n) ((n) << 4) ++#define USB_CNTL_BURST_0 (0 << 9) ++#define USB_CNTL_BURST_4 (1 << 9) ++#define USB_CNTL_BURST_8 (2 << 9) ++#define USB_CNTL_BURST_16 (3 << 9) ++ ++ ++#ifndef DEBUG ++# define DEBUG(fmt,args...) do {} while(0) ++#endif ++#ifndef DEBUG_EP0 ++# define NO_STATES ++# define DEBUG_EP0(fmt,args...) do {} while(0) ++#endif ++#ifndef DEBUG_SETUP ++# define DEBUG_SETUP(fmt,args...) do {} while(0) ++#endif ++ ++/* ++ * Local declarations. ++ */ ++static void jz4740_ep0_kick(struct jz4740_udc *dev, struct jz4740_ep *ep); ++static void jz4740_handle_ep0(struct jz4740_udc *dev, uint32_t intr); ++ ++static void done(struct jz4740_ep *ep, struct jz4740_request *req, ++ int status); ++static void pio_irq_enable(struct jz4740_ep *ep); ++static void pio_irq_disable(struct jz4740_ep *ep); ++static void stop_activity(struct jz4740_udc *dev, ++ struct usb_gadget_driver *driver); ++static void nuke(struct jz4740_ep *ep, int status); ++static void flush(struct jz4740_ep *ep); ++static void udc_set_address(struct jz4740_udc *dev, unsigned char address); ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* inline functions of register read/write/set/clear */ ++ ++static inline uint8_t usb_readb(struct jz4740_udc *udc, size_t reg) ++{ ++ return readb(udc->base + reg); ++} ++ ++static inline uint16_t usb_readw(struct jz4740_udc *udc, size_t reg) ++{ ++ return readw(udc->base + reg); ++} ++ ++static inline uint32_t usb_readl(struct jz4740_udc *udc, size_t reg) ++{ ++ return readl(udc->base + reg); ++} ++ ++static inline void usb_writeb(struct jz4740_udc *udc, size_t reg, uint8_t val) ++{ ++ writeb(val, udc->base + reg); ++} ++ ++static inline void usb_writew(struct jz4740_udc *udc, size_t reg, uint16_t val) ++{ ++ writew(val, udc->base + reg); ++} ++ ++static inline void usb_writel(struct jz4740_udc *udc, size_t reg, uint32_t val) ++{ ++ writel(val, udc->base + reg); ++} ++ ++static inline void usb_setb(struct jz4740_udc *udc, size_t reg, uint8_t mask) ++{ ++ usb_writeb(udc, reg, usb_readb(udc, reg) | mask); ++} ++ ++static inline void usb_setw(struct jz4740_udc *udc, size_t reg, uint16_t mask) ++{ ++ usb_writew(udc, reg, usb_readw(udc, reg) | mask); ++} ++ ++static inline void usb_clearb(struct jz4740_udc *udc, size_t reg, uint8_t mask) ++{ ++ usb_writeb(udc, reg, usb_readb(udc, reg) & ~mask); ++} ++ ++static inline void usb_clearw(struct jz4740_udc *udc, size_t reg, uint16_t mask) ++{ ++ usb_writew(udc, reg, usb_readw(udc, reg) & ~mask); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static inline void jz_udc_set_index(struct jz4740_udc *udc, uint8_t index) ++{ ++ usb_writeb(udc, JZ_REG_UDC_INDEX, index); ++} ++ ++static inline void jz_udc_select_ep(struct jz4740_ep *ep) ++{ ++ jz_udc_set_index(ep->dev, ep_index(ep)); ++} ++ ++static inline int write_packet(struct jz4740_ep *ep, ++ struct jz4740_request *req, unsigned int count) ++{ ++ uint8_t *buf; ++ unsigned int length; ++ void __iomem *fifo = ep->dev->base + ep->fifo; ++ ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ ++ buf = req->req.buf + req->req.actual; ++ ++ length = req->req.length - req->req.actual; ++ if (length > count) ++ length = count; ++ req->req.actual += length; ++ ++ DEBUG("Write %d (count %d), fifo %x\n", length, count, ep->fifo); ++ ++ writesl(fifo, buf, length >> 2); ++ writesb(fifo, &buf[length - (length & 3)], length & 3); ++ ++ return length; ++} ++ ++static int read_packet(struct jz4740_ep *ep, ++ struct jz4740_request *req, unsigned int count) ++{ ++ uint8_t *buf; ++ unsigned int length; ++ void __iomem *fifo = ep->dev->base + ep->fifo; ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ ++ buf = req->req.buf + req->req.actual; ++ ++ length = req->req.length - req->req.actual; ++ if (length > count) ++ length = count; ++ req->req.actual += length; ++ ++ DEBUG("Read %d, fifo %x\n", length, ep->fifo); ++ ++ readsl(fifo, buf, length >> 2); ++ readsb(fifo, &buf[length - (length & 3)], length & 3); ++ ++ return length; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * udc_disable - disable USB device controller ++ */ ++static void udc_disable(struct jz4740_udc *dev) ++{ ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ ++ udc_set_address(dev, 0); ++ ++ /* Disable interrupts */ ++ usb_writew(dev, JZ_REG_UDC_INTRINE, 0); ++ usb_writew(dev, JZ_REG_UDC_INTROUTE, 0); ++ usb_writeb(dev, JZ_REG_UDC_INTRUSBE, 0); ++ ++ /* Disable DMA */ ++ usb_writel(dev, JZ_REG_UDC_CNTL1, 0); ++ usb_writel(dev, JZ_REG_UDC_CNTL2, 0); ++ ++ /* Disconnect from usb */ ++ usb_clearb(dev, JZ_REG_UDC_POWER, USB_POWER_SOFTCONN); ++ ++ /* Disable the USB PHY */ ++ clk_disable_unprepare(dev->clk); ++ ++ dev->ep0state = WAIT_FOR_SETUP; ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ return; ++} ++ ++/* ++ * udc_reinit - initialize software state ++ */ ++static void udc_reinit(struct jz4740_udc *dev) ++{ ++ int i; ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ ++ /* device/ep0 records init */ ++ INIT_LIST_HEAD(&dev->gadget.ep_list); ++ INIT_LIST_HEAD(&dev->gadget.ep0->ep_list); ++ dev->ep0state = WAIT_FOR_SETUP; ++ ++ for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { ++ struct jz4740_ep *ep = &dev->ep[i]; ++ ++ if (i != 0) ++ list_add_tail(&ep->ep.ep_list, &dev->gadget.ep_list); ++ ++ INIT_LIST_HEAD(&ep->queue); ++ ep->desc = 0; ++ ep->stopped = 0; ++ } ++} ++ ++/* until it's enabled, this UDC should be completely invisible ++ * to any USB host. ++ */ ++static void udc_enable(struct jz4740_udc *dev) ++{ ++ int i; ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ ++ /* UDC state is incorrect - Added by River */ ++ if (dev->state != UDC_STATE_ENABLE) { ++ return; ++ } ++ ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ /* Flush FIFO for each */ ++ for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { ++ struct jz4740_ep *ep = &dev->ep[i]; ++ ++ jz_udc_select_ep(ep); ++ flush(ep); ++ } ++ ++ /* Set this bit to allow the UDC entering low-power mode when ++ * there are no actions on the USB bus. ++ * UDC still works during this bit was set. ++ */ ++ jz4740_clock_udc_enable_auto_suspend(); ++ ++ /* Enable the USB PHY */ ++ clk_prepare_enable(dev->clk); ++ ++ /* Disable interrupts */ ++/* usb_writew(dev, JZ_REG_UDC_INTRINE, 0); ++ usb_writew(dev, JZ_REG_UDC_INTROUTE, 0); ++ usb_writeb(dev, JZ_REG_UDC_INTRUSBE, 0);*/ ++ ++ /* Enable interrupts */ ++ usb_setw(dev, JZ_REG_UDC_INTRINE, USB_INTR_EP0); ++ usb_setb(dev, JZ_REG_UDC_INTRUSBE, USB_INTR_RESET); ++ /* Don't enable rest of the interrupts */ ++ /* usb_setw(dev, JZ_REG_UDC_INTRINE, USB_INTR_INEP1 | USB_INTR_INEP2); ++ usb_setw(dev, JZ_REG_UDC_INTROUTE, USB_INTR_OUTEP1); */ ++ ++ /* Enable SUSPEND */ ++ /* usb_setb(dev, JZ_REG_UDC_POWER, USB_POWER_SUSPENDM); */ ++ ++ /* Enable HS Mode */ ++ usb_setb(dev, JZ_REG_UDC_POWER, USB_POWER_HSENAB); ++ ++ /* Let host detect UDC: ++ * Software must write a 1 to the PMR:USB_POWER_SOFTCONN bit to turn this ++ * transistor on and pull the USBDP pin HIGH. ++ */ ++ usb_setb(dev, JZ_REG_UDC_POWER, USB_POWER_SOFTCONN); ++ ++ return; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* keeping it simple: ++ * - one bus driver, initted first; ++ * - one function driver, initted second ++ */ ++ ++/* ++ * Register entry point for the peripheral controller driver. ++ */ ++ ++static int jz4740_udc_start(struct usb_gadget *gadget, ++ struct usb_gadget_driver *driver) ++{ ++ struct jz4740_udc *udc = container_of(gadget, struct jz4740_udc, gadget); ++ ++ /* hook up the driver */ ++ udc->driver = driver; ++ ++ ++ /* then enable host detection and ep0; and we're ready ++ * for set_configuration as well as eventual disconnect. ++ */ ++ udc_enable(udc); ++ ++ DEBUG("%s: registered gadget driver '%s'\n", gadget->name, ++ driver->driver.name); ++ ++ return 0; ++} ++ ++static void stop_activity(struct jz4740_udc *dev, ++ struct usb_gadget_driver *driver) ++{ ++ int i; ++ ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ ++ /* don't disconnect drivers more than once */ ++ if (dev->gadget.speed == USB_SPEED_UNKNOWN) ++ driver = 0; ++ dev->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ /* prevent new request submissions, kill any outstanding requests */ ++ for (i = 0; i < UDC_MAX_ENDPOINTS; i++) { ++ struct jz4740_ep *ep = &dev->ep[i]; ++ ++ ep->stopped = 1; ++ ++ jz_udc_select_ep(ep); ++ nuke(ep, -ESHUTDOWN); ++ } ++ ++ /* report disconnect; the driver is already quiesced */ ++ if (driver) { ++ spin_unlock(&dev->lock); ++ driver->disconnect(&dev->gadget); ++ spin_lock(&dev->lock); ++ } ++ ++ /* re-init driver-visible data structures */ ++ udc_reinit(dev); ++} ++ ++ ++/* ++ * Unregister entry point for the peripheral controller driver. ++ */ ++static int jz4740_udc_stop(struct usb_gadget *gadget, ++ struct usb_gadget_driver *driver) ++{ ++ struct jz4740_udc *udc = container_of(gadget, struct jz4740_udc, gadget); ++ unsigned long flags; ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ ++ spin_lock_irqsave(&udc->lock, flags); ++ udc->driver = NULL; ++ stop_activity(udc, driver); ++ spin_unlock_irqrestore(&udc->lock, flags); ++ ++ udc_disable(udc); ++ ++ DEBUG("unregistered driver '%s'\n", driver->driver.name); ++ ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/** Write request to FIFO (max write == maxp size) ++ * Return: 0 = still running, 1 = completed, negative = errno ++ * NOTE: INDEX register must be set for EP ++ */ ++static int write_fifo(struct jz4740_ep *ep, struct jz4740_request *req) ++{ ++ struct jz4740_udc *dev = ep->dev; ++ uint32_t max, csr; ++ ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ max = le16_to_cpu(ep->desc->wMaxPacketSize); ++ ++ csr = usb_readb(dev, ep->csr); ++ ++ if (!(csr & USB_INCSR_FFNOTEMPT)) { ++ unsigned count; ++ int is_last, is_short; ++ ++ count = write_packet(ep, req, max); ++ usb_setb(dev, ep->csr, USB_INCSR_INPKTRDY); ++ ++ /* last packet is usually short (or a zlp) */ ++ if (unlikely(count != max)) ++ is_last = is_short = 1; ++ else { ++ if (likely(req->req.length != req->req.actual) ++ || req->req.zero) ++ is_last = 0; ++ else ++ is_last = 1; ++ /* interrupt/iso maxpacket may not fill the fifo */ ++ is_short = unlikely(max < ep_maxpacket(ep)); ++ } ++ ++ DEBUG("%s: wrote %s %d bytes%s%s %d left %p\n", __FUNCTION__, ++ ep->ep.name, count, ++ is_last ? "/L" : "", is_short ? "/S" : "", ++ req->req.length - req->req.actual, req); ++ ++ /* requests complete when all IN data is in the FIFO */ ++ if (is_last) { ++ done(ep, req, 0); ++ if (list_empty(&ep->queue)) { ++ pio_irq_disable(ep); ++ } ++ return 1; ++ } ++ } else { ++ DEBUG("Hmm.. %d ep FIFO is not empty!\n", ep_index(ep)); ++ } ++ ++ return 0; ++} ++ ++/** Read to request from FIFO (max read == bytes in fifo) ++ * Return: 0 = still running, 1 = completed, negative = errno ++ * NOTE: INDEX register must be set for EP ++ */ ++static int read_fifo(struct jz4740_ep *ep, struct jz4740_request *req) ++{ ++ struct jz4740_udc *dev = ep->dev; ++ uint32_t csr; ++ unsigned count, is_short; ++ ++ /* make sure there's a packet in the FIFO. */ ++ csr = usb_readb(dev, ep->csr); ++ if (!(csr & USB_OUTCSR_OUTPKTRDY)) { ++ DEBUG("%s: Packet NOT ready!\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ /* read all bytes from this packet */ ++ count = usb_readw(dev, JZ_REG_UDC_OUTCOUNT); ++ ++ is_short = (count < ep->ep.maxpacket); ++ ++ count = read_packet(ep, req, count); ++ ++ DEBUG("read %s %02x, %d bytes%s req %p %d/%d\n", ++ ep->ep.name, csr, count, ++ is_short ? "/S" : "", req, req->req.actual, req->req.length); ++ ++ /* Clear OutPktRdy */ ++ usb_clearb(dev, ep->csr, USB_OUTCSR_OUTPKTRDY); ++ ++ /* completion */ ++ if (is_short || req->req.actual == req->req.length) { ++ done(ep, req, 0); ++ ++ if (list_empty(&ep->queue)) ++ pio_irq_disable(ep); ++ return 1; ++ } ++ ++ /* finished that packet. the next one may be waiting... */ ++ return 0; ++} ++ ++/* ++ * done - retire a request; caller blocked irqs ++ * INDEX register is preserved to keep same ++ */ ++static void done(struct jz4740_ep *ep, struct jz4740_request *req, int status) ++{ ++ unsigned int stopped = ep->stopped; ++ uint32_t index; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, ep); ++ list_del_init(&req->queue); ++ ++ if (likely(req->req.status == -EINPROGRESS)) ++ req->req.status = status; ++ else ++ status = req->req.status; ++ ++ if (status && status != -ESHUTDOWN) ++ DEBUG("complete %s req %p stat %d len %u/%u\n", ++ ep->ep.name, &req->req, status, ++ req->req.actual, req->req.length); ++ ++ /* don't modify queue heads during completion callback */ ++ ep->stopped = 1; ++ /* Read current index (completion may modify it) */ ++ index = usb_readb(ep->dev, JZ_REG_UDC_INDEX); ++ spin_unlock_irqrestore(&ep->dev->lock, ep->dev->lock_flags); ++ ++ req->req.complete(&ep->ep, &req->req); ++ ++ spin_lock_irqsave(&ep->dev->lock, ep->dev->lock_flags); ++ /* Restore index */ ++ jz_udc_set_index(ep->dev, index); ++ ep->stopped = stopped; ++} ++ ++static inline unsigned int jz4740_udc_ep_irq_enable_reg(struct jz4740_ep *ep) ++{ ++ if (ep_is_in(ep)) ++ return JZ_REG_UDC_INTRINE; ++ else ++ return JZ_REG_UDC_INTROUTE; ++} ++ ++/** Enable EP interrupt */ ++static void pio_irq_enable(struct jz4740_ep *ep) ++{ ++ DEBUG("%s: EP%d %s\n", __FUNCTION__, ep_index(ep), ep_is_in(ep) ? "IN": "OUT"); ++ ++ usb_setw(ep->dev, jz4740_udc_ep_irq_enable_reg(ep), BIT(ep_index(ep))); ++} ++ ++/** Disable EP interrupt */ ++static void pio_irq_disable(struct jz4740_ep *ep) ++{ ++ DEBUG("%s: EP%d %s\n", __FUNCTION__, ep_index(ep), ep_is_in(ep) ? "IN": "OUT"); ++ ++ usb_clearw(ep->dev, jz4740_udc_ep_irq_enable_reg(ep), BIT(ep_index(ep))); ++} ++ ++/* ++ * nuke - dequeue ALL requests ++ */ ++static void nuke(struct jz4740_ep *ep, int status) ++{ ++ struct jz4740_request *req; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, ep); ++ ++ /* Flush FIFO */ ++ flush(ep); ++ ++ /* called with irqs blocked */ ++ while (!list_empty(&ep->queue)) { ++ req = list_entry(ep->queue.next, struct jz4740_request, queue); ++ done(ep, req, status); ++ } ++ ++ /* Disable IRQ if EP is enabled (has descriptor) */ ++ if (ep->desc) ++ pio_irq_disable(ep); ++} ++ ++/** Flush EP FIFO ++ * NOTE: INDEX register must be set before this call ++ */ ++static void flush(struct jz4740_ep *ep) ++{ ++ DEBUG("%s: %s\n", __FUNCTION__, ep->ep.name); ++ ++ switch (ep->type) { ++ case ep_bulk_in: ++ case ep_interrupt: ++ usb_setb(ep->dev, ep->csr, USB_INCSR_FF); ++ break; ++ case ep_bulk_out: ++ usb_setb(ep->dev, ep->csr, USB_OUTCSR_FF); ++ break; ++ case ep_control: ++ break; ++ } ++} ++ ++/** ++ * jz4740_in_epn - handle IN interrupt ++ */ ++static void jz4740_in_epn(struct jz4740_udc *dev, uint32_t ep_idx, uint32_t intr) ++{ ++ uint32_t csr; ++ struct jz4740_ep *ep = &dev->ep[ep_idx + 1]; ++ struct jz4740_request *req; ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ ++ jz_udc_select_ep(ep); ++ ++ csr = usb_readb(dev, ep->csr); ++ DEBUG("%s: %d, csr %x\n", __FUNCTION__, ep_idx, csr); ++ ++ if (csr & USB_INCSR_SENTSTALL) { ++ DEBUG("USB_INCSR_SENTSTALL\n"); ++ usb_clearb(dev, ep->csr, USB_INCSR_SENTSTALL); ++ return; ++ } ++ ++ if (!ep->desc) { ++ DEBUG("%s: NO EP DESC\n", __FUNCTION__); ++ return; ++ } ++ ++ if (!list_empty(&ep->queue)) { ++ req = list_first_entry(&ep->queue, struct jz4740_request, queue); ++ write_fifo(ep, req); ++ } ++} ++ ++/* ++ * Bulk OUT (recv) ++ */ ++static void jz4740_out_epn(struct jz4740_udc *dev, uint32_t ep_idx, uint32_t intr) ++{ ++ struct jz4740_ep *ep = &dev->ep[ep_idx]; ++ struct jz4740_request *req; ++ ++ DEBUG("%s: %d\n", __FUNCTION__, ep_idx); ++ ++ jz_udc_select_ep(ep); ++ if (ep->desc) { ++ uint32_t csr; ++ ++ while ((csr = usb_readb(dev, ep->csr)) & ++ (USB_OUTCSR_OUTPKTRDY | USB_OUTCSR_SENTSTALL)) { ++ DEBUG("%s: %x\n", __FUNCTION__, csr); ++ ++ if (csr & USB_OUTCSR_SENTSTALL) { ++ DEBUG("%s: stall sent, flush fifo\n", ++ __FUNCTION__); ++ /* usb_set(USB_OUT_CSR1_FIFO_FLUSH, ep->csr1); */ ++ flush(ep); ++ } else if (csr & USB_OUTCSR_OUTPKTRDY) { ++ if (list_empty(&ep->queue)) ++ req = 0; ++ else ++ req = ++ list_entry(ep->queue.next, ++ struct jz4740_request, ++ queue); ++ ++ if (!req) { ++ DEBUG("%s: NULL REQ %d\n", ++ __FUNCTION__, ep_idx); ++ break; ++ } else { ++ read_fifo(ep, req); ++ } ++ } ++ } ++ } else { ++ /* Throw packet away.. */ ++ DEBUG("%s: ep %p ep_indx %d No descriptor?!?\n", __FUNCTION__, ep, ep_idx); ++ flush(ep); ++ } ++} ++ ++/** Halt specific EP ++ * Return 0 if success ++ * NOTE: Sets INDEX register to EP ! ++ */ ++static int jz4740_set_halt(struct usb_ep *_ep, int value) ++{ ++ struct jz4740_udc *dev; ++ struct jz4740_ep *ep; ++ unsigned long flags; ++ ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (unlikely(!_ep || (!ep->desc && ep->type != ep_control))) { ++ DEBUG("%s, bad ep\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ dev = ep->dev; ++ ++ spin_lock_irqsave(&dev->lock, flags); ++ ++ jz_udc_select_ep(ep); ++ ++ DEBUG("%s, ep %d, val %d\n", __FUNCTION__, ep_index(ep), value); ++ ++ if (ep_index(ep) == 0) { ++ /* EP0 */ ++ usb_setb(dev, JZ_REG_UDC_CSR0, USB_CSR0_SENDSTALL); ++ } else if (ep_is_in(ep)) { ++ uint32_t csr = usb_readb(dev, ep->csr); ++ if (value && ((csr & USB_INCSR_FFNOTEMPT) ++ || !list_empty(&ep->queue))) { ++ /* ++ * Attempts to halt IN endpoints will fail (returning -EAGAIN) ++ * if any transfer requests are still queued, or if the controller ++ * FIFO still holds bytes that the host hasnÂ’t collected. ++ */ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ DEBUG ++ ("Attempt to halt IN endpoint failed (returning -EAGAIN) %d %d\n", ++ (csr & USB_INCSR_FFNOTEMPT), ++ !list_empty(&ep->queue)); ++ return -EAGAIN; ++ } ++ flush(ep); ++ if (value) { ++ usb_setb(dev, ep->csr, USB_INCSR_SENDSTALL); ++ } else { ++ usb_clearb(dev, ep->csr, USB_INCSR_SENDSTALL); ++ usb_setb(dev, ep->csr, USB_INCSR_CDT); ++ } ++ } else { ++ ++ flush(ep); ++ if (value) { ++ usb_setb(dev, ep->csr, USB_OUTCSR_SENDSTALL); ++ } else { ++ usb_clearb(dev, ep->csr, USB_OUTCSR_SENDSTALL); ++ usb_setb(dev, ep->csr, USB_OUTCSR_CDT); ++ } ++ } ++ ++ ep->stopped = value; ++ ++ spin_unlock_irqrestore(&dev->lock, flags); ++ ++ DEBUG("%s %s halted\n", _ep->name, value == 0 ? "NOT" : "IS"); ++ ++ return 0; ++} ++ ++ ++static int jz4740_ep_enable(struct usb_ep *_ep, ++ const struct usb_endpoint_descriptor *desc) ++{ ++ struct jz4740_ep *ep; ++ struct jz4740_udc *dev; ++ unsigned long flags; ++ uint32_t max, csrh = 0; ++ ++ DEBUG("%s: trying to enable %s\n", __FUNCTION__, _ep->name); ++ ++ if (!_ep || !desc) ++ return -EINVAL; ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (ep->desc || ep->type == ep_control ++ || desc->bDescriptorType != USB_DT_ENDPOINT ++ || ep->bEndpointAddress != desc->bEndpointAddress) { ++ DEBUG("%s, bad ep or descriptor\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ /* xfer types must match, except that interrupt ~= bulk */ ++ if (ep->bmAttributes != desc->bmAttributes ++ && ep->bmAttributes != USB_ENDPOINT_XFER_BULK ++ && desc->bmAttributes != USB_ENDPOINT_XFER_INT) { ++ DEBUG("%s, %s type mismatch\n", __FUNCTION__, _ep->name); ++ return -EINVAL; ++ } ++ ++ dev = ep->dev; ++ if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN) { ++ DEBUG("%s, bogus device state\n", __FUNCTION__); ++ return -ESHUTDOWN; ++ } ++ ++ max = le16_to_cpu(desc->wMaxPacketSize); ++ ++ spin_lock_irqsave(&ep->dev->lock, flags); ++ ++ /* Configure the endpoint */ ++ jz_udc_select_ep(ep); ++ if (ep_is_in(ep)) { ++ usb_writew(dev, JZ_REG_UDC_INMAXP, max); ++ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { ++ case USB_ENDPOINT_XFER_BULK: ++ case USB_ENDPOINT_XFER_INT: ++ csrh &= ~USB_INCSRH_ISO; ++ break; ++ case USB_ENDPOINT_XFER_ISOC: ++ csrh |= USB_INCSRH_ISO; ++ break; ++ } ++ usb_writeb(dev, JZ_REG_UDC_INCSRH, csrh); ++ } ++ else { ++ usb_writew(dev, JZ_REG_UDC_OUTMAXP, max); ++ switch (desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) { ++ case USB_ENDPOINT_XFER_BULK: ++ csrh &= ~USB_OUTCSRH_ISO; ++ break; ++ case USB_ENDPOINT_XFER_INT: ++ csrh &= ~USB_OUTCSRH_ISO; ++ csrh |= USB_OUTCSRH_DNYT; ++ break; ++ case USB_ENDPOINT_XFER_ISOC: ++ csrh |= USB_OUTCSRH_ISO; ++ break; ++ } ++ usb_writeb(dev, JZ_REG_UDC_OUTCSRH, csrh); ++ } ++ ++ ++ ep->stopped = 0; ++ ep->desc = desc; ++ ep->ep.maxpacket = max; ++ ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++ ++ /* Reset halt state (does flush) */ ++ jz4740_set_halt(_ep, 0); ++ ++ DEBUG("%s: enabled %s\n", __FUNCTION__, _ep->name); ++ ++ return 0; ++} ++ ++/** Disable EP ++ * NOTE: Sets INDEX register ++ */ ++static int jz4740_ep_disable(struct usb_ep *_ep) ++{ ++ struct jz4740_ep *ep; ++ unsigned long flags; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, _ep); ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (!_ep || !ep->desc) { ++ DEBUG("%s, %s not enabled\n", __FUNCTION__, ++ _ep ? ep->ep.name : NULL); ++ return -EINVAL; ++ } ++ ++ spin_lock_irqsave(&ep->dev->lock, flags); ++ ++ jz_udc_select_ep(ep); ++ ++ /* Nuke all pending requests (does flush) */ ++ nuke(ep, -ESHUTDOWN); ++ ++ /* Disable ep IRQ */ ++ pio_irq_disable(ep); ++ ++ ep->desc = 0; ++ ep->stopped = 1; ++ ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++ ++ DEBUG("%s: disabled %s\n", __FUNCTION__, _ep->name); ++ return 0; ++} ++ ++static struct usb_request *jz4740_alloc_request(struct usb_ep *ep, gfp_t gfp_flags) ++{ ++ struct jz4740_request *req; ++ ++ req = kzalloc(sizeof(*req), gfp_flags); ++ if (!req) ++ return NULL; ++ ++ INIT_LIST_HEAD(&req->queue); ++ ++ return &req->req; ++} ++ ++static void jz4740_free_request(struct usb_ep *ep, struct usb_request *_req) ++{ ++ struct jz4740_request *req; ++ ++ req = container_of(_req, struct jz4740_request, req); ++ WARN_ON(!list_empty(&req->queue)); ++ ++ kfree(req); ++} ++ ++/*--------------------------------------------------------------------*/ ++ ++/** Queue one request ++ * Kickstart transfer if needed ++ * NOTE: Sets INDEX register ++ */ ++static int jz4740_queue(struct usb_ep *_ep, struct usb_request *_req, ++ gfp_t gfp_flags) ++{ ++ struct jz4740_request *req; ++ struct jz4740_ep *ep; ++ struct jz4740_udc *dev; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, _ep); ++ ++ req = container_of(_req, struct jz4740_request, req); ++ if (unlikely ++ (!_req || !_req->complete || !_req->buf ++ || !list_empty(&req->queue))) { ++ DEBUG("%s, bad params\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (unlikely(!_ep || (!ep->desc && ep->type != ep_control))) { ++ DEBUG("%s, bad ep\n", __FUNCTION__); ++ return -EINVAL; ++ } ++ ++ dev = ep->dev; ++ if (unlikely(!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)) { ++ DEBUG("%s, bogus device state %p\n", __FUNCTION__, dev->driver); ++ return -ESHUTDOWN; ++ } ++ ++ DEBUG("%s queue req %p, len %d buf %p\n", _ep->name, _req, _req->length, ++ _req->buf); ++ ++ spin_lock_irqsave(&dev->lock, dev->lock_flags); ++ ++ _req->status = -EINPROGRESS; ++ _req->actual = 0; ++ ++ /* kickstart this i/o queue? */ ++ DEBUG("Add to %d Q %d %d\n", ep_index(ep), list_empty(&ep->queue), ++ ep->stopped); ++ if (list_empty(&ep->queue) && likely(!ep->stopped)) { ++ uint32_t csr; ++ ++ if (unlikely(ep_index(ep) == 0)) { ++ /* EP0 */ ++ list_add_tail(&req->queue, &ep->queue); ++ jz4740_ep0_kick(dev, ep); ++ req = 0; ++ } ++ else if (ep_is_in(ep)) { ++ /* EP1 & EP2 */ ++ jz_udc_select_ep(ep); ++ csr = usb_readb(dev, ep->csr); ++ pio_irq_enable(ep); ++ if (!(csr & USB_INCSR_FFNOTEMPT)) { ++ if (write_fifo(ep, req) == 1) ++ req = 0; ++ } ++ } else { ++ /* EP1 */ ++ jz_udc_select_ep(ep); ++ csr = usb_readb(dev, ep->csr); ++ pio_irq_enable(ep); ++ if (csr & USB_OUTCSR_OUTPKTRDY) { ++ if (read_fifo(ep, req) == 1) ++ req = 0; ++ } ++ } ++ } ++ ++ /* pio or dma irq handler advances the queue. */ ++ if (likely(req != 0)) ++ list_add_tail(&req->queue, &ep->queue); ++ ++ spin_unlock_irqrestore(&dev->lock, dev->lock_flags); ++ ++ return 0; ++} ++ ++/* dequeue JUST ONE request */ ++static int jz4740_dequeue(struct usb_ep *_ep, struct usb_request *_req) ++{ ++ struct jz4740_ep *ep; ++ struct jz4740_request *req; ++ unsigned long flags; ++ ++ DEBUG("%s, %p\n", __FUNCTION__, _ep); ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (!_ep || ep->type == ep_control) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&ep->dev->lock, flags); ++ ++ /* make sure it's actually queued on this endpoint */ ++ list_for_each_entry(req, &ep->queue, queue) { ++ if (&req->req == _req) ++ break; ++ } ++ if (&req->req != _req) { ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++ return -EINVAL; ++ } ++ done(ep, req, -ECONNRESET); ++ ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++ return 0; ++} ++ ++/** Return bytes in EP FIFO ++ * NOTE: Sets INDEX register to EP ++ */ ++static int jz4740_fifo_status(struct usb_ep *_ep) ++{ ++ uint32_t csr; ++ int count = 0; ++ struct jz4740_ep *ep; ++ unsigned long flags; ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (!_ep) { ++ DEBUG("%s, bad ep\n", __FUNCTION__); ++ return -ENODEV; ++ } ++ ++ DEBUG("%s, %d\n", __FUNCTION__, ep_index(ep)); ++ ++ /* LPD can't report unclaimed bytes from IN fifos */ ++ if (ep_is_in(ep)) ++ return -EOPNOTSUPP; ++ ++ spin_lock_irqsave(&ep->dev->lock, flags); ++ jz_udc_select_ep(ep); ++ ++ csr = usb_readb(ep->dev, ep->csr); ++ if (ep->dev->gadget.speed != USB_SPEED_UNKNOWN || ++ csr & 0x1) { ++ count = usb_readw(ep->dev, JZ_REG_UDC_OUTCOUNT); ++ } ++ ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++ ++ return count; ++} ++ ++/** Flush EP FIFO ++ * NOTE: Sets INDEX register to EP ++ */ ++static void jz4740_fifo_flush(struct usb_ep *_ep) ++{ ++ struct jz4740_ep *ep; ++ unsigned long flags; ++ ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ ++ ep = container_of(_ep, struct jz4740_ep, ep); ++ if (unlikely(!_ep || (!ep->desc && ep->type == ep_control))) { ++ DEBUG("%s, bad ep\n", __FUNCTION__); ++ return; ++ } ++ ++ spin_lock_irqsave(&ep->dev->lock, flags); ++ ++ jz_udc_select_ep(ep); ++ flush(ep); ++ ++ spin_unlock_irqrestore(&ep->dev->lock, flags); ++} ++ ++/****************************************************************/ ++/* End Point 0 related functions */ ++/****************************************************************/ ++ ++/* return: 0 = still running, 1 = completed, negative = errno */ ++static int write_fifo_ep0(struct jz4740_ep *ep, struct jz4740_request *req) ++{ ++ uint32_t max; ++ unsigned count; ++ int is_last; ++ ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ max = ep_maxpacket(ep); ++ ++ count = write_packet(ep, req, max); ++ ++ /* last packet is usually short (or a zlp) */ ++ if (unlikely(count != max)) ++ is_last = 1; ++ else { ++ if (likely(req->req.length != req->req.actual) || req->req.zero) ++ is_last = 0; ++ else ++ is_last = 1; ++ } ++ ++ DEBUG_EP0("%s: wrote %s %d bytes%s %d left %p\n", __FUNCTION__, ++ ep->ep.name, count, ++ is_last ? "/L" : "", req->req.length - req->req.actual, req); ++ ++ /* requests complete when all IN data is in the FIFO */ ++ if (is_last) { ++ done(ep, req, 0); ++ return 1; ++ } ++ ++ return 0; ++} ++ ++static inline int jz4740_fifo_read(struct jz4740_ep *ep, ++ unsigned char *cp, int max) ++{ ++ int bytes; ++ int count = usb_readw(ep->dev, JZ_REG_UDC_OUTCOUNT); ++ ++ if (count > max) ++ count = max; ++ bytes = count; ++ while (count--) ++ *cp++ = usb_readb(ep->dev, ep->fifo); ++ ++ return bytes; ++} ++ ++static inline void jz4740_fifo_write(struct jz4740_ep *ep, ++ unsigned char *cp, int count) ++{ ++ DEBUG("fifo_write: %d %d\n", ep_index(ep), count); ++ while (count--) ++ usb_writeb(ep->dev, ep->fifo, *cp++); ++} ++ ++static int read_fifo_ep0(struct jz4740_ep *ep, struct jz4740_request *req) ++{ ++ struct jz4740_udc *dev = ep->dev; ++ uint32_t csr; ++ uint8_t *buf; ++ unsigned bufferspace, count, is_short; ++ ++ DEBUG_EP0("%s\n", __FUNCTION__); ++ ++ csr = usb_readb(dev, JZ_REG_UDC_CSR0); ++ if (!(csr & USB_CSR0_OUTPKTRDY)) ++ return 0; ++ ++ buf = req->req.buf + req->req.actual; ++ prefetchw(buf); ++ bufferspace = req->req.length - req->req.actual; ++ ++ /* read all bytes from this packet */ ++ if (likely(csr & USB_CSR0_OUTPKTRDY)) { ++ count = usb_readw(dev, JZ_REG_UDC_OUTCOUNT); ++ req->req.actual += min(count, bufferspace); ++ } else /* zlp */ ++ count = 0; ++ ++ is_short = (count < ep->ep.maxpacket); ++ DEBUG_EP0("read %s %02x, %d bytes%s req %p %d/%d\n", ++ ep->ep.name, csr, count, ++ is_short ? "/S" : "", req, req->req.actual, req->req.length); ++ ++ while (likely(count-- != 0)) { ++ uint8_t byte = (uint8_t)usb_readl(dev, ep->fifo); ++ ++ if (unlikely(bufferspace == 0)) { ++ /* this happens when the driver's buffer ++ * is smaller than what the host sent. ++ * discard the extra data. ++ */ ++ if (req->req.status != -EOVERFLOW) ++ DEBUG_EP0("%s overflow %d\n", ep->ep.name, ++ count); ++ req->req.status = -EOVERFLOW; ++ } else { ++ *buf++ = byte; ++ bufferspace--; ++ } ++ } ++ ++ /* completion */ ++ if (is_short || req->req.actual == req->req.length) { ++ done(ep, req, 0); ++ return 1; ++ } ++ ++ /* finished that packet. the next one may be waiting... */ ++ return 0; ++} ++ ++/** ++ * udc_set_address - set the USB address for this device ++ * @address: ++ * ++ * Called from control endpoint function after it decodes a set address setup packet. ++ */ ++static void udc_set_address(struct jz4740_udc *dev, unsigned char address) ++{ ++ DEBUG_EP0("%s: %d\n", __FUNCTION__, address); ++ ++ usb_writeb(dev, JZ_REG_UDC_FADDR, address); ++} ++ ++/* ++ * DATA_STATE_RECV (USB_CSR0_OUTPKTRDY) ++ * - if error ++ * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL bits ++ * - else ++ * set USB_CSR0_SVDOUTPKTRDY bit ++ if last set USB_CSR0_DATAEND bit ++ */ ++static void jz4740_ep0_out(struct jz4740_udc *dev, uint32_t csr, int kickstart) ++{ ++ struct jz4740_request *req; ++ struct jz4740_ep *ep = &dev->ep[0]; ++ int ret; ++ ++ DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); ++ ++ if (list_empty(&ep->queue)) ++ req = 0; ++ else ++ req = list_entry(ep->queue.next, struct jz4740_request, queue); ++ ++ if (req) { ++ if (req->req.length == 0) { ++ DEBUG_EP0("ZERO LENGTH OUT!\n"); ++ usb_setb(dev, JZ_REG_UDC_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); ++ dev->ep0state = WAIT_FOR_SETUP; ++ return; ++ } else if (kickstart) { ++ usb_setb(dev, JZ_REG_UDC_CSR0, (USB_CSR0_SVDOUTPKTRDY)); ++ return; ++ } ++ ret = read_fifo_ep0(ep, req); ++ if (ret) { ++ /* Done! */ ++ DEBUG_EP0("%s: finished, waiting for status\n", ++ __FUNCTION__); ++ usb_setb(dev, JZ_REG_UDC_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); ++ dev->ep0state = WAIT_FOR_SETUP; ++ } else { ++ /* Not done yet.. */ ++ DEBUG_EP0("%s: not finished\n", __FUNCTION__); ++ usb_setb(dev, JZ_REG_UDC_CSR0, USB_CSR0_SVDOUTPKTRDY); ++ } ++ } else { ++ DEBUG_EP0("NO REQ??!\n"); ++ } ++} ++ ++/* ++ * DATA_STATE_XMIT ++ */ ++static int jz4740_ep0_in(struct jz4740_udc *dev, uint32_t csr) ++{ ++ struct jz4740_request *req; ++ struct jz4740_ep *ep = &dev->ep[0]; ++ int ret, need_zlp = 0; ++ ++ DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); ++ ++ if (list_empty(&ep->queue)) ++ req = 0; ++ else ++ req = list_entry(ep->queue.next, struct jz4740_request, queue); ++ ++ if (!req) { ++ DEBUG_EP0("%s: NULL REQ\n", __FUNCTION__); ++ return 0; ++ } ++ ++ if (req->req.length == 0) { ++ usb_setb(dev, JZ_REG_UDC_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); ++ dev->ep0state = WAIT_FOR_SETUP; ++ return 1; ++ } ++ ++ if (req->req.length - req->req.actual == EP0_MAXPACKETSIZE) { ++ /* Next write will end with the packet size, */ ++ /* so we need zero-length-packet */ ++ need_zlp = 1; ++ } ++ ++ ret = write_fifo_ep0(ep, req); ++ ++ if (ret == 1 && !need_zlp) { ++ /* Last packet */ ++ DEBUG_EP0("%s: finished, waiting for status\n", __FUNCTION__); ++ ++ usb_setb(dev, JZ_REG_UDC_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); ++ dev->ep0state = WAIT_FOR_SETUP; ++ } else { ++ DEBUG_EP0("%s: not finished\n", __FUNCTION__); ++ usb_setb(dev, JZ_REG_UDC_CSR0, USB_CSR0_INPKTRDY); ++ } ++ ++ if (need_zlp) { ++ DEBUG_EP0("%s: Need ZLP!\n", __FUNCTION__); ++ usb_setb(dev, JZ_REG_UDC_CSR0, USB_CSR0_INPKTRDY); ++ dev->ep0state = DATA_STATE_NEED_ZLP; ++ } ++ ++ return 1; ++} ++ ++static int jz4740_handle_get_status(struct jz4740_udc *dev, ++ struct usb_ctrlrequest *ctrl) ++{ ++ struct jz4740_ep *ep0 = &dev->ep[0]; ++ struct jz4740_ep *qep; ++ int reqtype = (ctrl->bRequestType & USB_RECIP_MASK); ++ uint16_t val = 0; ++ ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ ++ if (reqtype == USB_RECIP_INTERFACE) { ++ /* This is not supported. ++ * And according to the USB spec, this one does nothing.. ++ * Just return 0 ++ */ ++ DEBUG_SETUP("GET_STATUS: USB_RECIP_INTERFACE\n"); ++ } else if (reqtype == USB_RECIP_DEVICE) { ++ DEBUG_SETUP("GET_STATUS: USB_RECIP_DEVICE\n"); ++ val |= (1 << 0); /* Self powered */ ++ /*val |= (1<<1); *//* Remote wakeup */ ++ } else if (reqtype == USB_RECIP_ENDPOINT) { ++ int ep_num = (ctrl->wIndex & ~USB_DIR_IN); ++ ++ DEBUG_SETUP ++ ("GET_STATUS: USB_RECIP_ENDPOINT (%d), ctrl->wLength = %d\n", ++ ep_num, ctrl->wLength); ++ ++ if (ctrl->wLength > 2 || ep_num > 3) ++ return -EOPNOTSUPP; ++ ++ qep = &dev->ep[ep_num]; ++ if (ep_is_in(qep) != ((ctrl->wIndex & USB_DIR_IN) ? 1 : 0) ++ && ep_index(qep) != 0) { ++ return -EOPNOTSUPP; ++ } ++ ++ jz_udc_select_ep(qep); ++ ++ /* Return status on next IN token */ ++ switch (qep->type) { ++ case ep_control: ++ val = ++ (usb_readb(dev, qep->csr) & USB_CSR0_SENDSTALL) == ++ USB_CSR0_SENDSTALL; ++ break; ++ case ep_bulk_in: ++ case ep_interrupt: ++ val = ++ (usb_readb(dev, qep->csr) & USB_INCSR_SENDSTALL) == ++ USB_INCSR_SENDSTALL; ++ break; ++ case ep_bulk_out: ++ val = ++ (usb_readb(dev, qep->csr) & USB_OUTCSR_SENDSTALL) == ++ USB_OUTCSR_SENDSTALL; ++ break; ++ } ++ ++ /* Back to EP0 index */ ++ jz_udc_set_index(dev, 0); ++ ++ DEBUG_SETUP("GET_STATUS, ep: %d (%x), val = %d\n", ep_num, ++ ctrl->wIndex, val); ++ } else { ++ DEBUG_SETUP("Unknown REQ TYPE: %d\n", reqtype); ++ return -EOPNOTSUPP; ++ } ++ ++ /* Clear "out packet ready" */ ++ usb_setb(dev, JZ_REG_UDC_CSR0, USB_CSR0_SVDOUTPKTRDY); ++ /* Put status to FIFO */ ++ jz4740_fifo_write(ep0, (uint8_t *)&val, sizeof(val)); ++ /* Issue "In packet ready" */ ++ usb_setb(dev, JZ_REG_UDC_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); ++ ++ return 0; ++} ++ ++/* ++ * WAIT_FOR_SETUP (OUTPKTRDY) ++ * - read data packet from EP0 FIFO ++ * - decode command ++ * - if error ++ * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL bits ++ * - else ++ * set USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND bits ++ */ ++static void jz4740_ep0_setup(struct jz4740_udc *dev, uint32_t csr) ++{ ++ struct jz4740_ep *ep = &dev->ep[0]; ++ struct usb_ctrlrequest ctrl; ++ int i; ++ ++ DEBUG_SETUP("%s: %x\n", __FUNCTION__, csr); ++ ++ /* Nuke all previous transfers */ ++ nuke(ep, -EPROTO); ++ ++ /* read control req from fifo (8 bytes) */ ++ jz4740_fifo_read(ep, (unsigned char *)&ctrl, 8); ++ ++ DEBUG_SETUP("SETUP %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bRequestType, ctrl.bRequest, ++ ctrl.wValue, ctrl.wIndex, ctrl.wLength); ++ ++ /* Set direction of EP0 */ ++ if (likely(ctrl.bRequestType & USB_DIR_IN)) { ++ ep->bEndpointAddress |= USB_DIR_IN; ++ } else { ++ ep->bEndpointAddress &= ~USB_DIR_IN; ++ } ++ ++ /* Handle some SETUP packets ourselves */ ++ switch (ctrl.bRequest) { ++ case USB_REQ_SET_ADDRESS: ++ if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) ++ break; ++ ++ DEBUG_SETUP("USB_REQ_SET_ADDRESS (%d)\n", ctrl.wValue); ++ udc_set_address(dev, ctrl.wValue); ++ usb_setb(dev, JZ_REG_UDC_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); ++ return; ++ ++ case USB_REQ_SET_CONFIGURATION: ++ if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) ++ break; ++ ++ DEBUG_SETUP("USB_REQ_SET_CONFIGURATION (%d)\n", ctrl.wValue); ++/* usb_setb(JZ_REG_UDC_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND));*/ ++ ++ /* Enable RESUME and SUSPEND interrupts */ ++ usb_setb(dev, JZ_REG_UDC_INTRUSBE, (USB_INTR_RESUME | USB_INTR_SUSPEND)); ++ break; ++ ++ case USB_REQ_SET_INTERFACE: ++ if (ctrl.bRequestType != (USB_TYPE_STANDARD | USB_RECIP_DEVICE)) ++ break; ++ ++ DEBUG_SETUP("USB_REQ_SET_INTERFACE (%d)\n", ctrl.wValue); ++/* usb_setb(JZ_REG_UDC_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND));*/ ++ break; ++ ++ case USB_REQ_GET_STATUS: ++ if (jz4740_handle_get_status(dev, &ctrl) == 0) ++ return; ++ ++ case USB_REQ_CLEAR_FEATURE: ++ case USB_REQ_SET_FEATURE: ++ if (ctrl.bRequestType == USB_RECIP_ENDPOINT) { ++ struct jz4740_ep *qep; ++ int ep_num = (ctrl.wIndex & 0x0f); ++ ++ /* Support only HALT feature */ ++ if (ctrl.wValue != 0 || ctrl.wLength != 0 ++ || ep_num > 3 || ep_num < 1) ++ break; ++ ++ qep = &dev->ep[ep_num]; ++ spin_unlock(&dev->lock); ++ if (ctrl.bRequest == USB_REQ_SET_FEATURE) { ++ DEBUG_SETUP("SET_FEATURE (%d)\n", ++ ep_num); ++ jz4740_set_halt(&qep->ep, 1); ++ } else { ++ DEBUG_SETUP("CLR_FEATURE (%d)\n", ++ ep_num); ++ jz4740_set_halt(&qep->ep, 0); ++ } ++ spin_lock(&dev->lock); ++ ++ jz_udc_set_index(dev, 0); ++ ++ /* Reply with a ZLP on next IN token */ ++ usb_setb(dev, JZ_REG_UDC_CSR0, ++ (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND)); ++ return; ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ /* gadget drivers see class/vendor specific requests, ++ * {SET,GET}_{INTERFACE,DESCRIPTOR,CONFIGURATION}, ++ * and more. ++ */ ++ if (dev->driver) { ++ /* device-2-host (IN) or no data setup command, process immediately */ ++ spin_unlock(&dev->lock); ++ ++ i = dev->driver->setup(&dev->gadget, &ctrl); ++ spin_lock(&dev->lock); ++ ++ if (unlikely(i < 0)) { ++ /* setup processing failed, force stall */ ++ DEBUG_SETUP ++ (" --> ERROR: gadget setup FAILED (stalling), setup returned %d\n", ++ i); ++ jz_udc_set_index(dev, 0); ++ usb_setb(dev, JZ_REG_UDC_CSR0, (USB_CSR0_SVDOUTPKTRDY | USB_CSR0_DATAEND | USB_CSR0_SENDSTALL)); ++ ++ /* ep->stopped = 1; */ ++ dev->ep0state = WAIT_FOR_SETUP; ++ } ++ else { ++ DEBUG_SETUP("gadget driver setup ok (%d)\n", ctrl.wLength); ++/* if (!ctrl.wLength) { ++ usb_setb(JZ_REG_UDC_CSR0, USB_CSR0_SVDOUTPKTRDY); ++ }*/ ++ } ++ } ++} ++ ++/* ++ * DATA_STATE_NEED_ZLP ++ */ ++static void jz4740_ep0_in_zlp(struct jz4740_udc *dev, uint32_t csr) ++{ ++ DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); ++ ++ usb_setb(dev, JZ_REG_UDC_CSR0, (USB_CSR0_INPKTRDY | USB_CSR0_DATAEND)); ++ dev->ep0state = WAIT_FOR_SETUP; ++} ++ ++/* ++ * handle ep0 interrupt ++ */ ++static void jz4740_handle_ep0(struct jz4740_udc *dev, uint32_t intr) ++{ ++ struct jz4740_ep *ep = &dev->ep[0]; ++ uint32_t csr; ++ ++ DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); ++ /* Set index 0 */ ++ jz_udc_set_index(dev, 0); ++ csr = usb_readb(dev, JZ_REG_UDC_CSR0); ++ ++ DEBUG_EP0("%s: csr = %x state = \n", __FUNCTION__, csr);//, state_names[dev->ep0state]); ++ ++ /* ++ * if SENT_STALL is set ++ * - clear the SENT_STALL bit ++ */ ++ if (csr & USB_CSR0_SENTSTALL) { ++ DEBUG_EP0("%s: USB_CSR0_SENTSTALL is set: %x\n", __FUNCTION__, csr); ++ usb_clearb(dev, JZ_REG_UDC_CSR0, USB_CSR0_SENDSTALL | USB_CSR0_SENTSTALL); ++ nuke(ep, -ECONNABORTED); ++ dev->ep0state = WAIT_FOR_SETUP; ++ return; ++ } ++ ++ /* ++ * if a transfer is in progress && INPKTRDY and OUTPKTRDY are clear ++ * - fill EP0 FIFO ++ * - if last packet ++ * - set IN_PKT_RDY | DATA_END ++ * - else ++ * set IN_PKT_RDY ++ */ ++ if (!(csr & (USB_CSR0_INPKTRDY | USB_CSR0_OUTPKTRDY))) { ++ DEBUG_EP0("%s: INPKTRDY and OUTPKTRDY are clear\n", ++ __FUNCTION__); ++ ++ switch (dev->ep0state) { ++ case DATA_STATE_XMIT: ++ DEBUG_EP0("continue with DATA_STATE_XMIT\n"); ++ jz4740_ep0_in(dev, csr); ++ return; ++ case DATA_STATE_NEED_ZLP: ++ DEBUG_EP0("continue with DATA_STATE_NEED_ZLP\n"); ++ jz4740_ep0_in_zlp(dev, csr); ++ return; ++ default: ++ /* Stall? */ ++// DEBUG_EP0("Odd state!! state = %s\n", ++// state_names[dev->ep0state]); ++ dev->ep0state = WAIT_FOR_SETUP; ++ /* nuke(ep, 0); */ ++ /* usb_setb(ep->csr, USB_CSR0_SENDSTALL); */ ++// break; ++ return; ++ } ++ } ++ ++ /* ++ * if SETUPEND is set ++ * - abort the last transfer ++ * - set SERVICED_SETUP_END_BIT ++ */ ++ if (csr & USB_CSR0_SETUPEND) { ++ DEBUG_EP0("%s: USB_CSR0_SETUPEND is set: %x\n", __FUNCTION__, csr); ++ ++ usb_setb(dev, JZ_REG_UDC_CSR0, USB_CSR0_SVDSETUPEND); ++ nuke(ep, 0); ++ dev->ep0state = WAIT_FOR_SETUP; ++ } ++ ++ /* ++ * if USB_CSR0_OUTPKTRDY is set ++ * - read data packet from EP0 FIFO ++ * - decode command ++ * - if error ++ * set SVDOUTPKTRDY | DATAEND | SENDSTALL bits ++ * - else ++ * set SVDOUTPKTRDY | DATAEND bits ++ */ ++ if (csr & USB_CSR0_OUTPKTRDY) { ++ ++ DEBUG_EP0("%s: EP0_OUT_PKT_RDY is set: %x\n", __FUNCTION__, ++ csr); ++ ++ switch (dev->ep0state) { ++ case WAIT_FOR_SETUP: ++ DEBUG_EP0("WAIT_FOR_SETUP\n"); ++ jz4740_ep0_setup(dev, csr); ++ break; ++ ++ case DATA_STATE_RECV: ++ DEBUG_EP0("DATA_STATE_RECV\n"); ++ jz4740_ep0_out(dev, csr, 0); ++ break; ++ ++ default: ++ /* send stall? */ ++ DEBUG_EP0("strange state!! 2. send stall? state = %d\n", ++ dev->ep0state); ++ break; ++ } ++ } ++} ++ ++static void jz4740_ep0_kick(struct jz4740_udc *dev, struct jz4740_ep *ep) ++{ ++ uint32_t csr; ++ ++ jz_udc_set_index(dev, 0); ++ ++ DEBUG_EP0("%s: %x\n", __FUNCTION__, csr); ++ ++ /* Clear "out packet ready" */ ++ ++ if (ep_is_in(ep)) { ++ usb_setb(dev, JZ_REG_UDC_CSR0, USB_CSR0_SVDOUTPKTRDY); ++ csr = usb_readb(dev, JZ_REG_UDC_CSR0); ++ dev->ep0state = DATA_STATE_XMIT; ++ jz4740_ep0_in(dev, csr); ++ } else { ++ csr = usb_readb(dev, JZ_REG_UDC_CSR0); ++ dev->ep0state = DATA_STATE_RECV; ++ jz4740_ep0_out(dev, csr, 1); ++ } ++} ++ ++/** Handle USB RESET interrupt ++ */ ++static void jz4740_reset_irq(struct jz4740_udc *dev) ++{ ++ dev->gadget.speed = (usb_readb(dev, JZ_REG_UDC_POWER) & USB_POWER_HSMODE) ? ++ USB_SPEED_HIGH : USB_SPEED_FULL; ++ ++ DEBUG_SETUP("%s: address = %d, speed = %s\n", __FUNCTION__, 0, ++ (dev->gadget.speed == USB_SPEED_HIGH) ? "HIGH":"FULL" ); ++} ++ ++/* ++ * jz4740 usb device interrupt handler. ++ */ ++static irqreturn_t jz4740_udc_irq(int irq, void *devid) ++{ ++ struct jz4740_udc *jz4740_udc = devid; ++ uint8_t index; ++ ++ uint32_t intr_usb = usb_readb(jz4740_udc, JZ_REG_UDC_INTRUSB) & 0x7; /* mask SOF */ ++ uint32_t intr_in = usb_readw(jz4740_udc, JZ_REG_UDC_INTRIN); ++ uint32_t intr_out = usb_readw(jz4740_udc, JZ_REG_UDC_INTROUT); ++ uint32_t intr_dma = usb_readb(jz4740_udc, JZ_REG_UDC_INTR); ++ ++ if (!intr_usb && !intr_in && !intr_out && !intr_dma) ++ return IRQ_HANDLED; ++ ++ ++ DEBUG("intr_out=%x intr_in=%x intr_usb=%x\n", ++ intr_out, intr_in, intr_usb); ++ ++ spin_lock(&jz4740_udc->lock); ++ index = usb_readb(jz4740_udc, JZ_REG_UDC_INDEX); ++ ++ /* Check for resume from suspend mode */ ++ if ((intr_usb & USB_INTR_RESUME) && ++ (usb_readb(jz4740_udc, JZ_REG_UDC_INTRUSBE) & USB_INTR_RESUME)) { ++ DEBUG("USB resume\n"); ++ jz4740_udc->driver->resume(&jz4740_udc->gadget); /* We have suspend(), so we must have resume() too. */ ++ } ++ ++ /* Check for system interrupts */ ++ if (intr_usb & USB_INTR_RESET) { ++ DEBUG("USB reset\n"); ++ jz4740_reset_irq(jz4740_udc); ++ } ++ ++ /* Check for endpoint 0 interrupt */ ++ if (intr_in & USB_INTR_EP0) { ++ DEBUG("USB_INTR_EP0 (control)\n"); ++ jz4740_handle_ep0(jz4740_udc, intr_in); ++ } ++ ++ /* Check for Bulk-IN DMA interrupt */ ++ if (intr_dma & 0x1) { ++ int ep_num; ++ struct jz4740_ep *ep; ++ ep_num = (usb_readl(jz4740_udc, JZ_REG_UDC_CNTL1) >> 4) & 0xf; ++ ep = &jz4740_udc->ep[ep_num + 1]; ++ jz_udc_select_ep(ep); ++ usb_setb(jz4740_udc, ep->csr, USB_INCSR_INPKTRDY); ++/* jz4740_in_epn(jz4740_udc, ep_num, intr_in);*/ ++ } ++ ++ /* Check for Bulk-OUT DMA interrupt */ ++ if (intr_dma & 0x2) { ++ int ep_num; ++ ep_num = (usb_readl(jz4740_udc, JZ_REG_UDC_CNTL2) >> 4) & 0xf; ++ jz4740_out_epn(jz4740_udc, ep_num, intr_out); ++ } ++ ++ /* Check for each configured endpoint interrupt */ ++ if (intr_in & USB_INTR_INEP1) { ++ DEBUG("USB_INTR_INEP1\n"); ++ jz4740_in_epn(jz4740_udc, 1, intr_in); ++ } ++ ++ if (intr_in & USB_INTR_INEP2) { ++ DEBUG("USB_INTR_INEP2\n"); ++ jz4740_in_epn(jz4740_udc, 2, intr_in); ++ } ++ ++ if (intr_out & USB_INTR_OUTEP1) { ++ DEBUG("USB_INTR_OUTEP1\n"); ++ jz4740_out_epn(jz4740_udc, 1, intr_out); ++ } ++ ++ /* Check for suspend mode */ ++ if ((intr_usb & USB_INTR_SUSPEND) && ++ (usb_readb(jz4740_udc, JZ_REG_UDC_INTRUSBE) & USB_INTR_SUSPEND)) { ++ DEBUG("USB suspend\n"); ++ jz4740_udc->driver->suspend(&jz4740_udc->gadget); ++ /* Host unloaded from us, can do something, such as flushing ++ the NAND block cache etc. */ ++ } ++ ++ jz_udc_set_index(jz4740_udc, index); ++ ++ spin_unlock(&jz4740_udc->lock); ++ ++ return IRQ_HANDLED; ++} ++ ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++ ++static inline struct jz4740_udc *gadget_to_udc(struct usb_gadget *gadget) ++{ ++ return container_of(gadget, struct jz4740_udc, gadget); ++} ++ ++static int jz4740_udc_get_frame(struct usb_gadget *_gadget) ++{ ++ DEBUG("%s, %p\n", __FUNCTION__, _gadget); ++ return usb_readw(gadget_to_udc(_gadget), JZ_REG_UDC_FRAME); ++} ++ ++static int jz4740_udc_wakeup(struct usb_gadget *_gadget) ++{ ++ /* host may not have enabled remote wakeup */ ++ /*if ((UDCCS0 & UDCCS0_DRWF) == 0) ++ return -EHOSTUNREACH; ++ udc_set_mask_UDCCR(UDCCR_RSM); */ ++ return -ENOTSUPP; ++} ++ ++static int jz4740_udc_pullup(struct usb_gadget *_gadget, int on) ++{ ++ struct jz4740_udc *udc = gadget_to_udc(_gadget); ++ unsigned long flags; ++ ++ local_irq_save(flags); ++ ++ if (on) { ++ udc->state = UDC_STATE_ENABLE; ++ udc_enable(udc); ++ } else { ++ udc->state = UDC_STATE_DISABLE; ++ udc_disable(udc); ++ } ++ ++ local_irq_restore(flags); ++ ++ return 0; ++} ++ ++static const struct usb_gadget_ops jz4740_udc_ops = { ++ .get_frame = jz4740_udc_get_frame, ++ .wakeup = jz4740_udc_wakeup, ++ .pullup = jz4740_udc_pullup, ++ .udc_start = jz4740_udc_start, ++ .udc_stop = jz4740_udc_stop, ++}; ++ ++static struct usb_ep_ops jz4740_ep_ops = { ++ .enable = jz4740_ep_enable, ++ .disable = jz4740_ep_disable, ++ ++ .alloc_request = jz4740_alloc_request, ++ .free_request = jz4740_free_request, ++ ++ .queue = jz4740_queue, ++ .dequeue = jz4740_dequeue, ++ ++ .set_halt = jz4740_set_halt, ++ .fifo_status = jz4740_fifo_status, ++ .fifo_flush = jz4740_fifo_flush, ++}; ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct jz4740_udc jz4740_udc_controller = { ++ .gadget = { ++ .ops = &jz4740_udc_ops, ++ .ep0 = &jz4740_udc_controller.ep[0].ep, ++ .max_speed = USB_SPEED_HIGH, ++ .name = "jz4740-udc", ++ .dev = { ++ .init_name = "gadget", ++ }, ++ }, ++ ++ /* control endpoint */ ++ .ep[0] = { ++ .ep = { ++ .name = "ep0", ++ .ops = &jz4740_ep_ops, ++ .maxpacket = EP0_MAXPACKETSIZE, ++ }, ++ .dev = &jz4740_udc_controller, ++ ++ .bEndpointAddress = 0, ++ .bmAttributes = 0, ++ ++ .type = ep_control, ++ .fifo = JZ_REG_UDC_EP_FIFO(0), ++ .csr = JZ_REG_UDC_CSR0, ++ }, ++ ++ /* bulk out endpoint */ ++ .ep[1] = { ++ .ep = { ++ .name = "ep1out-bulk", ++ .ops = &jz4740_ep_ops, ++ .maxpacket = EPBULK_MAXPACKETSIZE, ++ }, ++ .dev = &jz4740_udc_controller, ++ ++ .bEndpointAddress = 1, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ ++ .type = ep_bulk_out, ++ .fifo = JZ_REG_UDC_EP_FIFO(1), ++ .csr = JZ_REG_UDC_OUTCSR, ++ }, ++ ++ /* bulk in endpoint */ ++ .ep[2] = { ++ .ep = { ++ .name = "ep1in-bulk", ++ .ops = &jz4740_ep_ops, ++ .maxpacket = EPBULK_MAXPACKETSIZE, ++ }, ++ .dev = &jz4740_udc_controller, ++ ++ .bEndpointAddress = 1 | USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_BULK, ++ ++ .type = ep_bulk_in, ++ .fifo = JZ_REG_UDC_EP_FIFO(1), ++ .csr = JZ_REG_UDC_INCSR, ++ }, ++ ++ /* interrupt in endpoint */ ++ .ep[3] = { ++ .ep = { ++ .name = "ep2in-int", ++ .ops = &jz4740_ep_ops, ++ .maxpacket = EPINTR_MAXPACKETSIZE, ++ }, ++ .dev = &jz4740_udc_controller, ++ ++ .bEndpointAddress = 2 | USB_DIR_IN, ++ .bmAttributes = USB_ENDPOINT_XFER_INT, ++ ++ .type = ep_interrupt, ++ .fifo = JZ_REG_UDC_EP_FIFO(2), ++ .csr = JZ_REG_UDC_INCSR, ++ }, ++}; ++ ++static int jz4740_udc_probe(struct platform_device *pdev) ++{ ++ struct jz4740_udc *jz4740_udc = &jz4740_udc_controller; ++ int ret; ++ ++ spin_lock_init(&jz4740_udc->lock); ++ ++ jz4740_udc->dev = &pdev->dev; ++ ++ jz4740_udc->clk = clk_get(&pdev->dev, "udc"); ++ if (IS_ERR(jz4740_udc->clk)) { ++ ret = PTR_ERR(jz4740_udc->clk); ++ dev_err(&pdev->dev, "Failed to get udc clock: %d\n", ret); ++ return ret; ++ } ++ ++ platform_set_drvdata(pdev, jz4740_udc); ++ ++ jz4740_udc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ ++ if (!jz4740_udc->mem) { ++ ret = -ENOENT; ++ dev_err(&pdev->dev, "Failed to get mmio memory resource\n"); ++ goto err_clk_put; ++ } ++ ++ jz4740_udc->mem = request_mem_region(jz4740_udc->mem->start, ++ resource_size(jz4740_udc->mem), pdev->name); ++ ++ if (!jz4740_udc->mem) { ++ ret = -EBUSY; ++ dev_err(&pdev->dev, "Failed to request mmio memory region\n"); ++ goto err_clk_put; ++ } ++ ++ jz4740_udc->base = ioremap(jz4740_udc->mem->start, resource_size(jz4740_udc->mem)); ++ ++ if (!jz4740_udc->base) { ++ ret = -EBUSY; ++ dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); ++ goto err_release_mem_region; ++ } ++ ++ jz4740_udc->irq = platform_get_irq(pdev, 0); ++ ret = request_irq(jz4740_udc->irq, jz4740_udc_irq, 0, pdev->name, ++ jz4740_udc); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); ++ goto err_iounmap; ++ } ++ ++ ret = usb_add_gadget_udc(&pdev->dev, &jz4740_udc->gadget); ++ if (ret) { ++ dev_err(&pdev->dev, "Failed to add gadget: %d\n", ret); ++ goto err_free_irq; ++ } ++ ++/* udc_disable(jz4740_udc);*/ ++ udc_reinit(jz4740_udc); ++ ++ return 0; ++ ++err_free_irq: ++ free_irq(jz4740_udc->irq, pdev); ++err_iounmap: ++ iounmap(jz4740_udc->base); ++err_release_mem_region: ++ release_mem_region(jz4740_udc->mem->start, resource_size(jz4740_udc->mem)); ++err_clk_put: ++ clk_put(jz4740_udc->clk); ++ ++ return ret; ++} ++ ++static int jz4740_udc_remove(struct platform_device *pdev) ++{ ++ struct jz4740_udc *dev = platform_get_drvdata(pdev); ++ ++ usb_del_gadget_udc(&dev->gadget); ++ if (dev->driver) ++ return -EBUSY; ++ ++ udc_disable(dev); ++ ++ free_irq(dev->irq, dev); ++ iounmap(dev->base); ++ release_mem_region(dev->mem->start, resource_size(dev->mem)); ++ clk_put(dev->clk); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++ ++static int jz4740_udc_suspend(struct device *dev) ++{ ++ struct jz4740_udc *jz4740_udc = dev_get_drvdata(dev); ++ ++ if (jz4740_udc->state == UDC_STATE_ENABLE) ++ udc_disable(jz4740_udc); ++ ++ return 0; ++} ++ ++static int jz4740_udc_resume(struct device *dev) ++{ ++ struct jz4740_udc *jz4740_udc = dev_get_drvdata(dev); ++ ++ if (jz4740_udc->state == UDC_STATE_ENABLE) ++ udc_enable(jz4740_udc); ++ ++ return 0; ++} ++ ++static SIMPLE_DEV_PM_OPS(jz4740_udc_pm_ops, jz4740_udc_suspend, jz4740_udc_resume); ++#define JZ4740_UDC_PM_OPS (&jz4740_udc_pm_ops) ++ ++#else ++#define JZ4740_UDC_PM_OPS NULL ++#endif ++ ++static struct platform_driver udc_driver = { ++ .probe = jz4740_udc_probe, ++ .remove = jz4740_udc_remove, ++ .driver = { ++ .name = "jz-udc", ++ .owner = THIS_MODULE, ++ .pm = JZ4740_UDC_PM_OPS, ++ }, ++}; ++ ++/*-------------------------------------------------------------------------*/ ++ ++static int __init udc_init (void) ++{ ++ return platform_driver_register(&udc_driver); ++} ++module_init(udc_init); ++ ++static void __exit udc_exit (void) ++{ ++ platform_driver_unregister(&udc_driver); ++} ++module_exit(udc_exit); ++ ++MODULE_DESCRIPTION("JZ4740 USB Device Controller"); ++MODULE_AUTHOR("Wei Jianli <jlwei@ingenic.cn>"); ++MODULE_LICENSE("GPL"); +diff --git a/drivers/usb/gadget/jz4740_udc.h b/drivers/usb/gadget/jz4740_udc.h +new file mode 100644 +index 0000000..53fd1da +--- /dev/null ++++ b/drivers/usb/gadget/jz4740_udc.h +@@ -0,0 +1,101 @@ ++/* ++ * linux/drivers/usb/gadget/jz4740_udc.h ++ * ++ * Ingenic JZ4740 on-chip high speed USB device controller ++ * ++ * Copyright (C) 2006 Ingenic Semiconductor Inc. ++ * Author: <jlwei@ingenic.cn> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ */ ++ ++#ifndef __USB_GADGET_JZ4740_H__ ++#define __USB_GADGET_JZ4740_H__ ++ ++/*-------------------------------------------------------------------------*/ ++ ++// Max packet size ++#define EP0_MAXPACKETSIZE 64 ++#define EPBULK_MAXPACKETSIZE 512 ++#define EPINTR_MAXPACKETSIZE 64 ++ ++#define UDC_MAX_ENDPOINTS 4 ++ ++/*-------------------------------------------------------------------------*/ ++ ++enum ep_type { ++ ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt ++}; ++ ++struct jz4740_ep { ++ struct usb_ep ep; ++ struct jz4740_udc *dev; ++ ++ const struct usb_endpoint_descriptor *desc; ++ ++ uint8_t stopped; ++ uint8_t bEndpointAddress; ++ uint8_t bmAttributes; ++ ++ enum ep_type type; ++ size_t fifo; ++ uint32_t csr; ++ ++ uint32_t reg_addr; ++ struct list_head queue; ++}; ++ ++struct jz4740_request { ++ struct usb_request req; ++ struct list_head queue; ++}; ++ ++enum ep0state { ++ WAIT_FOR_SETUP, /* between STATUS ack and SETUP report */ ++ DATA_STATE_XMIT, /* data tx stage */ ++ DATA_STATE_NEED_ZLP, /* data tx zlp stage */ ++ WAIT_FOR_OUT_STATUS, /* status stages */ ++ DATA_STATE_RECV, /* data rx stage */ ++}; ++ ++/* For function binding with UDC Disable - Added by River */ ++typedef enum { ++ UDC_STATE_ENABLE = 0, ++ UDC_STATE_DISABLE, ++}udc_state_t; ++ ++struct jz4740_udc { ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *driver; ++ struct device *dev; ++ spinlock_t lock; ++ unsigned long lock_flags; ++ ++ enum ep0state ep0state; ++ struct jz4740_ep ep[UDC_MAX_ENDPOINTS]; ++ ++ udc_state_t state; ++ ++ struct resource *mem; ++ void __iomem *base; ++ int irq; ++ ++ struct clk *clk; ++}; ++ ++#define ep_maxpacket(EP) ((EP)->ep.maxpacket) ++ ++static inline bool ep_is_in(const struct jz4740_ep *ep) ++{ ++ return (ep->bEndpointAddress & USB_DIR_IN) == USB_DIR_IN; ++} ++ ++static inline uint8_t ep_index(const struct jz4740_ep *ep) ++{ ++ return ep->bEndpointAddress & 0xf; ++} ++ ++#endif /* __USB_GADGET_JZ4740_H__ */ +-- +1.7.10.4 + diff --git a/target/linux/xburst/patches-3.10/009-Add-ili8960-lcd-driver.patch b/target/linux/xburst/patches-3.10/009-Add-ili8960-lcd-driver.patch new file mode 100644 index 0000000..3984ffe --- /dev/null +++ b/target/linux/xburst/patches-3.10/009-Add-ili8960-lcd-driver.patch @@ -0,0 +1,314 @@ +From ee3a5c779be4d1d270388203cd1c6a734a0bf4ed 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 09/16] Add ili8960 lcd driver + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +--- + drivers/video/backlight/Kconfig | 7 + + drivers/video/backlight/Makefile | 1 + + drivers/video/backlight/ili8960.c | 262 +++++++++++++++++++++++++++++++++++++ + 3 files changed, 270 insertions(+) + create mode 100644 drivers/video/backlight/ili8960.c + +diff --git a/drivers/video/backlight/Kconfig b/drivers/video/backlight/Kconfig +index d5ab658..7cd68d0 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_ILI922X + tristate "ILI Technology ILI9221/ILI9222 support" + depends on SPI +diff --git a/drivers/video/backlight/Makefile b/drivers/video/backlight/Makefile +index 92711fe..81d3e93 100644 +--- a/drivers/video/backlight/Makefile ++++ b/drivers/video/backlight/Makefile +@@ -5,6 +5,7 @@ obj-$(CONFIG_LCD_CLASS_DEVICE) += lcd.o + obj-$(CONFIG_LCD_CORGI) += corgi_lcd.o + obj-$(CONFIG_LCD_HP700) += jornada720_lcd.o + obj-$(CONFIG_LCD_HX8357) += hx8357.o ++obj-$(CONFIG_LCD_ILI8960) += ili8960.o + obj-$(CONFIG_LCD_ILI922X) += ili922x.o + obj-$(CONFIG_LCD_ILI9320) += ili9320.o + obj-$(CONFIG_LCD_L4F00242T03) += l4f00242t03.o +diff --git a/drivers/video/backlight/ili8960.c b/drivers/video/backlight/ili8960.c +new file mode 100644 +index 0000000..61eb815 +--- /dev/null ++++ b/drivers/video/backlight/ili8960.c +@@ -0,0 +1,262 @@ ++/* ++ * 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/delay.h> ++ ++struct ili8960 { ++ struct spi_device *spi; ++ struct lcd_device *lcd; ++ 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: ++ return 0; ++ } ++ ++ return ili8960_programm_power(ili8960->spi, ili8960->enabled); ++} ++ ++static int ili8960_early_set_power(struct lcd_device *lcd, int power) ++{ ++ struct ili8960 *ili8960 = lcd_get_data(lcd); ++ ++ switch (power) { ++ case FB_BLANK_UNBLANK: ++ return 0; ++ 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 = kstrtoul(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, ++ .early_set_power = ili8960_early_set_power, ++ .get_power = ili8960_get_power, ++ .set_contrast = ili8960_set_contrast, ++ .set_mode = ili8960_set_mode, ++}; ++ ++static int ili8960_probe(struct spi_device *spi) ++{ ++ int ret; ++ struct ili8960 *ili8960; ++ ++ ili8960 = devm_kzalloc(&spi->dev, 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"); ++ return ret; ++ } ++ ++ 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); ++ return ret; ++ } ++ ++ 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); ++ return ret; ++} ++ ++static int 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); ++ 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 = ili8960_remove, ++ .suspend = ili8960_suspend, ++ .resume = ili8960_resume, ++}; ++module_spi_driver(ili8960_driver); ++ ++MODULE_AUTHOR("Lars-Peter Clausen"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("LCD driver for Ilitek ili8960"); ++MODULE_ALIAS("spi:ili8960"); +-- +1.7.10.4 + diff --git a/target/linux/xburst/patches-3.10/010-qi_lb60-Don-t-use-3-wire-spi-mode-for-the-display-fo.patch b/target/linux/xburst/patches-3.10/010-qi_lb60-Don-t-use-3-wire-spi-mode-for-the-display-fo.patch new file mode 100644 index 0000000..b717bd5 --- /dev/null +++ b/target/linux/xburst/patches-3.10/010-qi_lb60-Don-t-use-3-wire-spi-mode-for-the-display-fo.patch @@ -0,0 +1,28 @@ +From 590ceec7028084ebd9d643b4929ceae9fd806fbe 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 10/16] qi_lb60: Don't use 3-wire spi mode for the display for + now + +The spi_gpio driver does not support 3-wire mode. + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +--- + arch/mips/jz4740/board-qi_lb60.c | 1 - + 1 file changed, 1 deletion(-) + +diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c +index be2b3de..bc454e6 100644 +--- a/arch/mips/jz4740/board-qi_lb60.c ++++ b/arch/mips/jz4740/board-qi_lb60.c +@@ -311,7 +311,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.10.4 + diff --git a/target/linux/xburst/patches-3.10/011-dma-Add-a-jz4740-dmaengine-driver.patch b/target/linux/xburst/patches-3.10/011-dma-Add-a-jz4740-dmaengine-driver.patch new file mode 100644 index 0000000..3225c7b --- /dev/null +++ b/target/linux/xburst/patches-3.10/011-dma-Add-a-jz4740-dmaengine-driver.patch @@ -0,0 +1,488 @@ +From 8c53b6491806a37d6999886d22c34bfed310034c Mon Sep 17 00:00:00 2001 +From: Lars-Peter Clausen <lars@metafoo.de> +Date: Thu, 30 May 2013 18:25:02 +0200 +Subject: [PATCH 11/16] dma: Add a jz4740 dmaengine driver + +This patch adds dmaengine support for the JZ4740 DMA controller. For now the +driver will be a wrapper around the custom JZ4740 DMA API. Once all users of the +custom JZ4740 DMA API have been converted to the dmaengine API the custom API +will be removed and direct hardware access will be added to the dmaengine +driver. + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +Signed-off-by: Vinod Koul <vinod.koul@intel.com> +--- + drivers/dma/Kconfig | 6 + + drivers/dma/Makefile | 1 + + drivers/dma/dma-jz4740.c | 433 ++++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 440 insertions(+) + create mode 100644 drivers/dma/dma-jz4740.c + +diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig +index e992489..b3e8952 100644 +--- a/drivers/dma/Kconfig ++++ b/drivers/dma/Kconfig +@@ -312,6 +312,12 @@ config MMP_PDMA + help + Support the MMP PDMA engine for PXA and MMP platfrom. + ++config DMA_JZ4740 ++ tristate "JZ4740 DMA support" ++ depends on MACH_JZ4740 ++ select DMA_ENGINE ++ select DMA_VIRTUAL_CHANNELS ++ + config DMA_ENGINE + bool + +diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile +index a2b0df5..6127a61 100644 +--- a/drivers/dma/Makefile ++++ b/drivers/dma/Makefile +@@ -38,3 +38,4 @@ obj-$(CONFIG_DMA_SA11X0) += sa11x0-dma.o + obj-$(CONFIG_MMP_TDMA) += mmp_tdma.o + obj-$(CONFIG_DMA_OMAP) += omap-dma.o + obj-$(CONFIG_MMP_PDMA) += mmp_pdma.o ++obj-$(CONFIG_DMA_JZ4740) += dma-jz4740.o +diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c +new file mode 100644 +index 0000000..3d42434 +--- /dev/null ++++ b/drivers/dma/dma-jz4740.c +@@ -0,0 +1,433 @@ ++/* ++ * Copyright (C) 2013, Lars-Peter Clausen <lars@metafoo.de> ++ * JZ4740 DMAC support ++ * ++ * 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/dmaengine.h> ++#include <linux/dma-mapping.h> ++#include <linux/err.h> ++#include <linux/init.h> ++#include <linux/list.h> ++#include <linux/module.h> ++#include <linux/platform_device.h> ++#include <linux/slab.h> ++#include <linux/spinlock.h> ++ ++#include <asm/mach-jz4740/dma.h> ++ ++#include "virt-dma.h" ++ ++#define JZ_DMA_NR_CHANS 6 ++ ++struct jz4740_dma_sg { ++ dma_addr_t addr; ++ unsigned int len; ++}; ++ ++struct jz4740_dma_desc { ++ struct virt_dma_desc vdesc; ++ ++ enum dma_transfer_direction direction; ++ bool cyclic; ++ ++ unsigned int num_sgs; ++ struct jz4740_dma_sg sg[]; ++}; ++ ++struct jz4740_dmaengine_chan { ++ struct virt_dma_chan vchan; ++ struct jz4740_dma_chan *jz_chan; ++ ++ dma_addr_t fifo_addr; ++ ++ struct jz4740_dma_desc *desc; ++ unsigned int next_sg; ++}; ++ ++struct jz4740_dma_dev { ++ struct dma_device ddev; ++ ++ struct jz4740_dmaengine_chan chan[JZ_DMA_NR_CHANS]; ++}; ++ ++static struct jz4740_dmaengine_chan *to_jz4740_dma_chan(struct dma_chan *c) ++{ ++ return container_of(c, struct jz4740_dmaengine_chan, vchan.chan); ++} ++ ++static struct jz4740_dma_desc *to_jz4740_dma_desc(struct virt_dma_desc *vdesc) ++{ ++ return container_of(vdesc, struct jz4740_dma_desc, vdesc); ++} ++ ++static struct jz4740_dma_desc *jz4740_dma_alloc_desc(unsigned int num_sgs) ++{ ++ return kzalloc(sizeof(struct jz4740_dma_desc) + ++ sizeof(struct jz4740_dma_sg) * num_sgs, GFP_ATOMIC); ++} ++ ++static enum jz4740_dma_width jz4740_dma_width(enum dma_slave_buswidth width) ++{ ++ switch (width) { ++ case DMA_SLAVE_BUSWIDTH_1_BYTE: ++ return JZ4740_DMA_WIDTH_8BIT; ++ case DMA_SLAVE_BUSWIDTH_2_BYTES: ++ return JZ4740_DMA_WIDTH_16BIT; ++ case DMA_SLAVE_BUSWIDTH_4_BYTES: ++ return JZ4740_DMA_WIDTH_32BIT; ++ default: ++ return JZ4740_DMA_WIDTH_32BIT; ++ } ++} ++ ++static enum jz4740_dma_transfer_size jz4740_dma_maxburst(u32 maxburst) ++{ ++ if (maxburst <= 1) ++ return JZ4740_DMA_TRANSFER_SIZE_1BYTE; ++ else if (maxburst <= 3) ++ return JZ4740_DMA_TRANSFER_SIZE_2BYTE; ++ else if (maxburst <= 15) ++ return JZ4740_DMA_TRANSFER_SIZE_4BYTE; ++ else if (maxburst <= 31) ++ return JZ4740_DMA_TRANSFER_SIZE_16BYTE; ++ ++ return JZ4740_DMA_TRANSFER_SIZE_32BYTE; ++} ++ ++static int jz4740_dma_slave_config(struct dma_chan *c, ++ const struct dma_slave_config *config) ++{ ++ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); ++ struct jz4740_dma_config jzcfg; ++ ++ switch (config->direction) { ++ case DMA_MEM_TO_DEV: ++ jzcfg.flags = JZ4740_DMA_SRC_AUTOINC; ++ jzcfg.transfer_size = jz4740_dma_maxburst(config->dst_maxburst); ++ chan->fifo_addr = config->dst_addr; ++ break; ++ case DMA_DEV_TO_MEM: ++ jzcfg.flags = JZ4740_DMA_DST_AUTOINC; ++ jzcfg.transfer_size = jz4740_dma_maxburst(config->src_maxburst); ++ chan->fifo_addr = config->src_addr; ++ break; ++ default: ++ return -EINVAL; ++ } ++ ++ ++ jzcfg.src_width = jz4740_dma_width(config->src_addr_width); ++ jzcfg.dst_width = jz4740_dma_width(config->dst_addr_width); ++ jzcfg.mode = JZ4740_DMA_MODE_SINGLE; ++ jzcfg.request_type = config->slave_id; ++ ++ jz4740_dma_configure(chan->jz_chan, &jzcfg); ++ ++ return 0; ++} ++ ++static int jz4740_dma_terminate_all(struct dma_chan *c) ++{ ++ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); ++ unsigned long flags; ++ LIST_HEAD(head); ++ ++ spin_lock_irqsave(&chan->vchan.lock, flags); ++ jz4740_dma_disable(chan->jz_chan); ++ chan->desc = NULL; ++ vchan_get_all_descriptors(&chan->vchan, &head); ++ spin_unlock_irqrestore(&chan->vchan.lock, flags); ++ ++ vchan_dma_desc_free_list(&chan->vchan, &head); ++ ++ return 0; ++} ++ ++static int jz4740_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ++ unsigned long arg) ++{ ++ struct dma_slave_config *config = (struct dma_slave_config *)arg; ++ ++ switch (cmd) { ++ case DMA_SLAVE_CONFIG: ++ return jz4740_dma_slave_config(chan, config); ++ case DMA_TERMINATE_ALL: ++ return jz4740_dma_terminate_all(chan); ++ default: ++ return -ENOSYS; ++ } ++} ++ ++static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan) ++{ ++ dma_addr_t src_addr, dst_addr; ++ struct virt_dma_desc *vdesc; ++ struct jz4740_dma_sg *sg; ++ ++ jz4740_dma_disable(chan->jz_chan); ++ ++ if (!chan->desc) { ++ vdesc = vchan_next_desc(&chan->vchan); ++ if (!vdesc) ++ return 0; ++ chan->desc = to_jz4740_dma_desc(vdesc); ++ chan->next_sg = 0; ++ } ++ ++ if (chan->next_sg == chan->desc->num_sgs) ++ chan->next_sg = 0; ++ ++ sg = &chan->desc->sg[chan->next_sg]; ++ ++ if (chan->desc->direction == DMA_MEM_TO_DEV) { ++ src_addr = sg->addr; ++ dst_addr = chan->fifo_addr; ++ } else { ++ src_addr = chan->fifo_addr; ++ dst_addr = sg->addr; ++ } ++ jz4740_dma_set_src_addr(chan->jz_chan, src_addr); ++ jz4740_dma_set_dst_addr(chan->jz_chan, dst_addr); ++ jz4740_dma_set_transfer_count(chan->jz_chan, sg->len); ++ ++ chan->next_sg++; ++ ++ jz4740_dma_enable(chan->jz_chan); ++ ++ return 0; ++} ++ ++static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error, ++ void *devid) ++{ ++ struct jz4740_dmaengine_chan *chan = devid; ++ ++ spin_lock(&chan->vchan.lock); ++ if (chan->desc) { ++ if (chan->desc && chan->desc->cyclic) { ++ vchan_cyclic_callback(&chan->desc->vdesc); ++ } else { ++ if (chan->next_sg == chan->desc->num_sgs) { ++ chan->desc = NULL; ++ vchan_cookie_complete(&chan->desc->vdesc); ++ } ++ } ++ } ++ jz4740_dma_start_transfer(chan); ++ spin_unlock(&chan->vchan.lock); ++} ++ ++static void jz4740_dma_issue_pending(struct dma_chan *c) ++{ ++ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); ++ unsigned long flags; ++ ++ spin_lock_irqsave(&chan->vchan.lock, flags); ++ if (vchan_issue_pending(&chan->vchan) && !chan->desc) ++ jz4740_dma_start_transfer(chan); ++ spin_unlock_irqrestore(&chan->vchan.lock, flags); ++} ++ ++static struct dma_async_tx_descriptor *jz4740_dma_prep_slave_sg( ++ struct dma_chan *c, struct scatterlist *sgl, ++ unsigned int sg_len, enum dma_transfer_direction direction, ++ unsigned long flags, void *context) ++{ ++ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); ++ struct jz4740_dma_desc *desc; ++ struct scatterlist *sg; ++ unsigned int i; ++ ++ desc = jz4740_dma_alloc_desc(sg_len); ++ if (!desc) ++ return NULL; ++ ++ for_each_sg(sgl, sg, sg_len, i) { ++ desc->sg[i].addr = sg_dma_address(sg); ++ desc->sg[i].len = sg_dma_len(sg); ++ } ++ ++ desc->num_sgs = sg_len; ++ desc->direction = direction; ++ desc->cyclic = false; ++ ++ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); ++} ++ ++static struct dma_async_tx_descriptor *jz4740_dma_prep_dma_cyclic( ++ struct dma_chan *c, dma_addr_t buf_addr, size_t buf_len, ++ size_t period_len, enum dma_transfer_direction direction, ++ unsigned long flags, void *context) ++{ ++ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); ++ struct jz4740_dma_desc *desc; ++ unsigned int num_periods, i; ++ ++ if (buf_len % period_len) ++ return NULL; ++ ++ num_periods = buf_len / period_len; ++ ++ desc = jz4740_dma_alloc_desc(num_periods); ++ if (!desc) ++ return NULL; ++ ++ for (i = 0; i < num_periods; i++) { ++ desc->sg[i].addr = buf_addr; ++ desc->sg[i].len = period_len; ++ buf_addr += period_len; ++ } ++ ++ desc->num_sgs = num_periods; ++ desc->direction = direction; ++ desc->cyclic = true; ++ ++ return vchan_tx_prep(&chan->vchan, &desc->vdesc, flags); ++} ++ ++static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan, ++ struct jz4740_dma_desc *desc, unsigned int next_sg) ++{ ++ size_t residue = 0; ++ unsigned int i; ++ ++ residue = 0; ++ ++ for (i = next_sg; i < desc->num_sgs; i++) ++ residue += desc->sg[i].len; ++ ++ if (next_sg != 0) ++ residue += jz4740_dma_get_residue(chan->jz_chan); ++ ++ return residue; ++} ++ ++static enum dma_status jz4740_dma_tx_status(struct dma_chan *c, ++ dma_cookie_t cookie, struct dma_tx_state *state) ++{ ++ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); ++ struct virt_dma_desc *vdesc; ++ enum dma_status status; ++ unsigned long flags; ++ ++ status = dma_cookie_status(c, cookie, state); ++ if (status == DMA_SUCCESS || !state) ++ return status; ++ ++ spin_lock_irqsave(&chan->vchan.lock, flags); ++ vdesc = vchan_find_desc(&chan->vchan, cookie); ++ if (cookie == chan->desc->vdesc.tx.cookie) { ++ state->residue = jz4740_dma_desc_residue(chan, chan->desc, ++ chan->next_sg); ++ } else if (vdesc) { ++ state->residue = jz4740_dma_desc_residue(chan, ++ to_jz4740_dma_desc(vdesc), 0); ++ } else { ++ state->residue = 0; ++ } ++ spin_unlock_irqrestore(&chan->vchan.lock, flags); ++ ++ return status; ++} ++ ++static int jz4740_dma_alloc_chan_resources(struct dma_chan *c) ++{ ++ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); ++ ++ chan->jz_chan = jz4740_dma_request(chan, NULL); ++ if (!chan->jz_chan) ++ return -EBUSY; ++ ++ jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb); ++ ++ return 0; ++} ++ ++static void jz4740_dma_free_chan_resources(struct dma_chan *c) ++{ ++ struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); ++ ++ vchan_free_chan_resources(&chan->vchan); ++ jz4740_dma_free(chan->jz_chan); ++ chan->jz_chan = NULL; ++} ++ ++static void jz4740_dma_desc_free(struct virt_dma_desc *vdesc) ++{ ++ kfree(container_of(vdesc, struct jz4740_dma_desc, vdesc)); ++} ++ ++static int jz4740_dma_probe(struct platform_device *pdev) ++{ ++ struct jz4740_dmaengine_chan *chan; ++ struct jz4740_dma_dev *dmadev; ++ struct dma_device *dd; ++ unsigned int i; ++ int ret; ++ ++ dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL); ++ if (!dmadev) ++ return -EINVAL; ++ ++ dd = &dmadev->ddev; ++ ++ dma_cap_set(DMA_SLAVE, dd->cap_mask); ++ dma_cap_set(DMA_CYCLIC, dd->cap_mask); ++ dd->device_alloc_chan_resources = jz4740_dma_alloc_chan_resources; ++ dd->device_free_chan_resources = jz4740_dma_free_chan_resources; ++ dd->device_tx_status = jz4740_dma_tx_status; ++ dd->device_issue_pending = jz4740_dma_issue_pending; ++ dd->device_prep_slave_sg = jz4740_dma_prep_slave_sg; ++ dd->device_prep_dma_cyclic = jz4740_dma_prep_dma_cyclic; ++ dd->device_control = jz4740_dma_control; ++ dd->dev = &pdev->dev; ++ dd->chancnt = JZ_DMA_NR_CHANS; ++ INIT_LIST_HEAD(&dd->channels); ++ ++ for (i = 0; i < dd->chancnt; i++) { ++ chan = &dmadev->chan[i]; ++ chan->vchan.desc_free = jz4740_dma_desc_free; ++ vchan_init(&chan->vchan, dd); ++ } ++ ++ ret = dma_async_device_register(dd); ++ if (ret) ++ return ret; ++ ++ platform_set_drvdata(pdev, dmadev); ++ ++ return 0; ++} ++ ++static int jz4740_dma_remove(struct platform_device *pdev) ++{ ++ struct jz4740_dma_dev *dmadev = platform_get_drvdata(pdev); ++ ++ dma_async_device_unregister(&dmadev->ddev); ++ ++ return 0; ++} ++ ++static struct platform_driver jz4740_dma_driver = { ++ .probe = jz4740_dma_probe, ++ .remove = jz4740_dma_remove, ++ .driver = { ++ .name = "jz4740-dma", ++ .owner = THIS_MODULE, ++ }, ++}; ++module_platform_driver(jz4740_dma_driver); ++ ++MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>"); ++MODULE_DESCRIPTION("JZ4740 DMA driver"); ++MODULE_LICENSE("GPLv2"); +-- +1.7.10.4 + diff --git a/target/linux/xburst/patches-3.10/012-MIPS-JZ4740-Correct-clock-gate-bit-for-DMA-controlle.patch b/target/linux/xburst/patches-3.10/012-MIPS-JZ4740-Correct-clock-gate-bit-for-DMA-controlle.patch new file mode 100644 index 0000000..06850ec --- /dev/null +++ b/target/linux/xburst/patches-3.10/012-MIPS-JZ4740-Correct-clock-gate-bit-for-DMA-controlle.patch @@ -0,0 +1,27 @@ +From 1a0086ae912b81a1e6c7213e97059c50a7bfe636 Mon Sep 17 00:00:00 2001 +From: Maarten ter Huurne <maarten@treewalker.org> +Date: Sun, 6 May 2012 01:51:09 +0200 +Subject: [PATCH 12/16] MIPS: JZ4740: Correct clock gate bit for DMA + controller + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +--- + arch/mips/jz4740/clock.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/arch/mips/jz4740/clock.c b/arch/mips/jz4740/clock.c +index 484d38a..1b5f554 100644 +--- a/arch/mips/jz4740/clock.c ++++ b/arch/mips/jz4740/clock.c +@@ -687,7 +687,7 @@ static struct clk jz4740_clock_simple_clks[] = { + [3] = { + .name = "dma", + .parent = &jz_clk_high_speed_peripheral.clk, +- .gate_bit = JZ_CLOCK_GATE_UART0, ++ .gate_bit = JZ_CLOCK_GATE_DMAC, + .ops = &jz_clk_simple_ops, + }, + [4] = { +-- +1.7.10.4 + diff --git a/target/linux/xburst/patches-3.10/013-MIPS-JZ4740-Acquire-and-enable-DMA-controller-clock.patch b/target/linux/xburst/patches-3.10/013-MIPS-JZ4740-Acquire-and-enable-DMA-controller-clock.patch new file mode 100644 index 0000000..110f061 --- /dev/null +++ b/target/linux/xburst/patches-3.10/013-MIPS-JZ4740-Acquire-and-enable-DMA-controller-clock.patch @@ -0,0 +1,80 @@ +From b1ab71156cd49b2389397d3be54b93fe394a1cb2 Mon Sep 17 00:00:00 2001 +From: Maarten ter Huurne <maarten@treewalker.org> +Date: Tue, 9 Oct 2012 13:09:57 +0200 +Subject: [PATCH 13/16] MIPS: JZ4740: Acquire and enable DMA controller clock + +Previously, it was assumed that the DMA controller clock is not gated +when the kernel starts running. While that is the power-on state, it is +safer to not rely on that. + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +--- + arch/mips/jz4740/clock.c | 2 +- + arch/mips/jz4740/dma.c | 24 ++++++++++++++++++++++-- + 2 files changed, 23 insertions(+), 3 deletions(-) + +diff --git a/arch/mips/jz4740/clock.c b/arch/mips/jz4740/clock.c +index 1b5f554..b3eba60 100644 +--- a/arch/mips/jz4740/clock.c ++++ b/arch/mips/jz4740/clock.c +@@ -921,4 +921,4 @@ static int jz4740_clock_init(void) + + return 0; + } +-arch_initcall(jz4740_clock_init); ++postcore_initcall(jz4740_clock_init); +diff --git a/arch/mips/jz4740/dma.c b/arch/mips/jz4740/dma.c +index 317ec6f..fb5266c 100644 +--- a/arch/mips/jz4740/dma.c ++++ b/arch/mips/jz4740/dma.c +@@ -16,6 +16,7 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/spinlock.h> ++#include <linux/clk.h> + #include <linux/interrupt.h> + + #include <linux/dma-mapping.h> +@@ -268,6 +269,7 @@ static irqreturn_t jz4740_dma_irq(int irq, void *dev_id) + + static int jz4740_dma_init(void) + { ++ struct clk *clk; + unsigned int ret; + + jz4740_dma_base = ioremap(JZ4740_DMAC_BASE_ADDR, 0x400); +@@ -277,11 +279,29 @@ static int jz4740_dma_init(void) + + spin_lock_init(&jz4740_dma_lock); + +- ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL); ++ clk = clk_get(NULL, "dma"); ++ if (IS_ERR(clk)) { ++ ret = PTR_ERR(clk); ++ printk(KERN_ERR "JZ4740 DMA: Failed to request clock: %d\n", ++ ret); ++ goto err_iounmap; ++ } + +- if (ret) ++ ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL); ++ if (ret) { + printk(KERN_ERR "JZ4740 DMA: Failed to request irq: %d\n", ret); ++ goto err_clkput; ++ } ++ ++ clk_enable(clk); ++ ++ return 0; ++ ++err_clkput: ++ clk_put(clk); + ++err_iounmap: ++ iounmap(jz4740_dma_base); + return ret; + } + arch_initcall(jz4740_dma_init); +-- +1.7.10.4 + diff --git a/target/linux/xburst/patches-3.10/014-MIPS-jz4740-Register-jz4740-DMA-device.patch b/target/linux/xburst/patches-3.10/014-MIPS-jz4740-Register-jz4740-DMA-device.patch new file mode 100644 index 0000000..1ae288b --- /dev/null +++ b/target/linux/xburst/patches-3.10/014-MIPS-jz4740-Register-jz4740-DMA-device.patch @@ -0,0 +1,73 @@ +From 734bdfd446b1eba0c54ad07703a192e6093417fd Mon Sep 17 00:00:00 2001 +From: Lars-Peter Clausen <lars@metafoo.de> +Date: Thu, 30 May 2013 18:25:03 +0200 +Subject: [PATCH 14/16] MIPS: jz4740: Register jz4740 DMA device + +Register a device for the newly added jz4740 dmaengine driver. + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +Acked-by: Ralf Baechle <ralf@linux-mips.org> +[manually edited to align struct assignment] +Signed-off-by: Vinod Koul <vinod.koul@intel.com> +--- + arch/mips/include/asm/mach-jz4740/platform.h | 1 + + arch/mips/jz4740/board-qi_lb60.c | 1 + + arch/mips/jz4740/platform.c | 21 +++++++++++++++++++++ + 3 files changed, 23 insertions(+) + +diff --git a/arch/mips/include/asm/mach-jz4740/platform.h b/arch/mips/include/asm/mach-jz4740/platform.h +index 72cfebd..05988c2 100644 +--- a/arch/mips/include/asm/mach-jz4740/platform.h ++++ b/arch/mips/include/asm/mach-jz4740/platform.h +@@ -32,6 +32,7 @@ extern struct platform_device jz4740_codec_device; + extern struct platform_device jz4740_adc_device; + extern struct platform_device jz4740_wdt_device; + extern struct platform_device jz4740_pwm_device; ++extern struct platform_device jz4740_dma_device; + + void jz4740_serial_device_register(void); + +diff --git a/arch/mips/jz4740/board-qi_lb60.c b/arch/mips/jz4740/board-qi_lb60.c +index bc454e6..b857521 100644 +--- a/arch/mips/jz4740/board-qi_lb60.c ++++ b/arch/mips/jz4740/board-qi_lb60.c +@@ -437,6 +437,7 @@ static struct platform_device *jz_platform_devices[] __initdata = { + &jz4740_rtc_device, + &jz4740_adc_device, + &jz4740_pwm_device, ++ &jz4740_dma_device, + &qi_lb60_gpio_keys, + &qi_lb60_pwm_beeper, + &qi_lb60_charger_device, +diff --git a/arch/mips/jz4740/platform.c b/arch/mips/jz4740/platform.c +index e9348fd..df65677 100644 +--- a/arch/mips/jz4740/platform.c ++++ b/arch/mips/jz4740/platform.c +@@ -329,3 +329,24 @@ struct platform_device jz4740_pwm_device = { + .name = "jz4740-pwm", + .id = -1, + }; ++ ++/* DMA */ ++static struct resource jz4740_dma_resources[] = { ++ { ++ .start = JZ4740_DMAC_BASE_ADDR, ++ .end = JZ4740_DMAC_BASE_ADDR + 0x400 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = JZ4740_IRQ_DMAC, ++ .end = JZ4740_IRQ_DMAC, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++struct platform_device jz4740_dma_device = { ++ .name = "jz4740-dma", ++ .id = -1, ++ .num_resources = ARRAY_SIZE(jz4740_dma_resources), ++ .resource = jz4740_dma_resources, ++}; +-- +1.7.10.4 + diff --git a/target/linux/xburst/patches-3.10/015-MIPS-jz4740-Remove-custom-DMA-API.patch b/target/linux/xburst/patches-3.10/015-MIPS-jz4740-Remove-custom-DMA-API.patch new file mode 100644 index 0000000..ff25552 --- /dev/null +++ b/target/linux/xburst/patches-3.10/015-MIPS-jz4740-Remove-custom-DMA-API.patch @@ -0,0 +1,854 @@ +From 7b91fca454e66ac1bc1fe5b68de0bf55f799bd41 Mon Sep 17 00:00:00 2001 +From: Lars-Peter Clausen <lars@metafoo.de> +Date: Thu, 30 May 2013 18:25:05 +0200 +Subject: [PATCH 15/16] MIPS: jz4740: Remove custom DMA API + +Now that all users of the custom jz4740 DMA API have been converted to use +the dmaengine API instead we can remove the custom API and move all the code +talking to the hardware to the dmaengine driver. + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +Acked-by: Ralf Baechle <ralf@linux-mips.org> +Signed-off-by: Vinod Koul <vinod.koul@intel.com> +--- + arch/mips/include/asm/mach-jz4740/dma.h | 56 ------ + arch/mips/jz4740/Makefile | 2 +- + arch/mips/jz4740/dma.c | 307 ------------------------------- + drivers/dma/dma-jz4740.c | 258 ++++++++++++++++++++++---- + 4 files changed, 222 insertions(+), 401 deletions(-) + delete mode 100644 arch/mips/jz4740/dma.c + +diff --git a/arch/mips/include/asm/mach-jz4740/dma.h b/arch/mips/include/asm/mach-jz4740/dma.h +index 98b4e7c..509cd58 100644 +--- a/arch/mips/include/asm/mach-jz4740/dma.h ++++ b/arch/mips/include/asm/mach-jz4740/dma.h +@@ -16,8 +16,6 @@ + #ifndef __ASM_MACH_JZ4740_DMA_H__ + #define __ASM_MACH_JZ4740_DMA_H__ + +-struct jz4740_dma_chan; +- + enum jz4740_dma_request_type { + JZ4740_DMA_TYPE_AUTO_REQUEST = 8, + JZ4740_DMA_TYPE_UART_TRANSMIT = 20, +@@ -33,58 +31,4 @@ enum jz4740_dma_request_type { + JZ4740_DMA_TYPE_SLCD = 30, + }; + +-enum jz4740_dma_width { +- JZ4740_DMA_WIDTH_32BIT = 0, +- JZ4740_DMA_WIDTH_8BIT = 1, +- JZ4740_DMA_WIDTH_16BIT = 2, +-}; +- +-enum jz4740_dma_transfer_size { +- JZ4740_DMA_TRANSFER_SIZE_4BYTE = 0, +- JZ4740_DMA_TRANSFER_SIZE_1BYTE = 1, +- JZ4740_DMA_TRANSFER_SIZE_2BYTE = 2, +- JZ4740_DMA_TRANSFER_SIZE_16BYTE = 3, +- JZ4740_DMA_TRANSFER_SIZE_32BYTE = 4, +-}; +- +-enum jz4740_dma_flags { +- JZ4740_DMA_SRC_AUTOINC = 0x2, +- JZ4740_DMA_DST_AUTOINC = 0x1, +-}; +- +-enum jz4740_dma_mode { +- JZ4740_DMA_MODE_SINGLE = 0, +- JZ4740_DMA_MODE_BLOCK = 1, +-}; +- +-struct jz4740_dma_config { +- enum jz4740_dma_width src_width; +- enum jz4740_dma_width dst_width; +- enum jz4740_dma_transfer_size transfer_size; +- enum jz4740_dma_request_type request_type; +- enum jz4740_dma_flags flags; +- enum jz4740_dma_mode mode; +-}; +- +-typedef void (*jz4740_dma_complete_callback_t)(struct jz4740_dma_chan *, int, void *); +- +-struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name); +-void jz4740_dma_free(struct jz4740_dma_chan *dma); +- +-void jz4740_dma_configure(struct jz4740_dma_chan *dma, +- const struct jz4740_dma_config *config); +- +- +-void jz4740_dma_enable(struct jz4740_dma_chan *dma); +-void jz4740_dma_disable(struct jz4740_dma_chan *dma); +- +-void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src); +-void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst); +-void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count); +- +-uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma); +- +-void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma, +- jz4740_dma_complete_callback_t cb); +- + #endif /* __ASM_JZ4740_DMA_H__ */ +diff --git a/arch/mips/jz4740/Makefile b/arch/mips/jz4740/Makefile +index 63bad0e..28e5535 100644 +--- a/arch/mips/jz4740/Makefile ++++ b/arch/mips/jz4740/Makefile +@@ -4,7 +4,7 @@ + + # Object file lists. + +-obj-y += prom.o irq.o time.o reset.o setup.o dma.o \ ++obj-y += prom.o irq.o time.o reset.o setup.o \ + gpio.o clock.o platform.o timer.o serial.o + + obj-$(CONFIG_DEBUG_FS) += clock-debugfs.o +diff --git a/arch/mips/jz4740/dma.c b/arch/mips/jz4740/dma.c +deleted file mode 100644 +index fb5266c..0000000 +--- a/arch/mips/jz4740/dma.c ++++ /dev/null +@@ -1,307 +0,0 @@ +-/* +- * Copyright (C) 2010, Lars-Peter Clausen <lars@metafoo.de> +- * JZ4740 SoC DMA support +- * +- * 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/kernel.h> +-#include <linux/module.h> +-#include <linux/spinlock.h> +-#include <linux/clk.h> +-#include <linux/interrupt.h> +- +-#include <linux/dma-mapping.h> +-#include <asm/mach-jz4740/dma.h> +-#include <asm/mach-jz4740/base.h> +- +-#define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20) +-#define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20) +-#define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20) +-#define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20) +-#define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20) +-#define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20) +-#define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20) +- +-#define JZ_REG_DMA_CTRL 0x300 +-#define JZ_REG_DMA_IRQ 0x304 +-#define JZ_REG_DMA_DOORBELL 0x308 +-#define JZ_REG_DMA_DOORBELL_SET 0x30C +- +-#define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31) +-#define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6) +-#define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4) +-#define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3) +-#define JZ_DMA_STATUS_CTRL_HALT BIT(2) +-#define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1) +-#define JZ_DMA_STATUS_CTRL_ENABLE BIT(0) +- +-#define JZ_DMA_CMD_SRC_INC BIT(23) +-#define JZ_DMA_CMD_DST_INC BIT(22) +-#define JZ_DMA_CMD_RDIL_MASK (0xf << 16) +-#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14) +-#define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12) +-#define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8) +-#define JZ_DMA_CMD_BLOCK_MODE BIT(7) +-#define JZ_DMA_CMD_DESC_VALID BIT(4) +-#define JZ_DMA_CMD_DESC_VALID_MODE BIT(3) +-#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2) +-#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1) +-#define JZ_DMA_CMD_LINK_ENABLE BIT(0) +- +-#define JZ_DMA_CMD_FLAGS_OFFSET 22 +-#define JZ_DMA_CMD_RDIL_OFFSET 16 +-#define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14 +-#define JZ_DMA_CMD_DST_WIDTH_OFFSET 12 +-#define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8 +-#define JZ_DMA_CMD_MODE_OFFSET 7 +- +-#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8) +-#define JZ_DMA_CTRL_HALT BIT(3) +-#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2) +-#define JZ_DMA_CTRL_ENABLE BIT(0) +- +- +-static void __iomem *jz4740_dma_base; +-static spinlock_t jz4740_dma_lock; +- +-static inline uint32_t jz4740_dma_read(size_t reg) +-{ +- return readl(jz4740_dma_base + reg); +-} +- +-static inline void jz4740_dma_write(size_t reg, uint32_t val) +-{ +- writel(val, jz4740_dma_base + reg); +-} +- +-static inline void jz4740_dma_write_mask(size_t reg, uint32_t val, uint32_t mask) +-{ +- uint32_t val2; +- val2 = jz4740_dma_read(reg); +- val2 &= ~mask; +- val2 |= val; +- jz4740_dma_write(reg, val2); +-} +- +-struct jz4740_dma_chan { +- unsigned int id; +- void *dev; +- const char *name; +- +- enum jz4740_dma_flags flags; +- uint32_t transfer_shift; +- +- jz4740_dma_complete_callback_t complete_cb; +- +- unsigned used:1; +-}; +- +-#define JZ4740_DMA_CHANNEL(_id) { .id = _id } +- +-struct jz4740_dma_chan jz4740_dma_channels[] = { +- JZ4740_DMA_CHANNEL(0), +- JZ4740_DMA_CHANNEL(1), +- JZ4740_DMA_CHANNEL(2), +- JZ4740_DMA_CHANNEL(3), +- JZ4740_DMA_CHANNEL(4), +- JZ4740_DMA_CHANNEL(5), +-}; +- +-struct jz4740_dma_chan *jz4740_dma_request(void *dev, const char *name) +-{ +- unsigned int i; +- struct jz4740_dma_chan *dma = NULL; +- +- spin_lock(&jz4740_dma_lock); +- +- for (i = 0; i < ARRAY_SIZE(jz4740_dma_channels); ++i) { +- if (!jz4740_dma_channels[i].used) { +- dma = &jz4740_dma_channels[i]; +- dma->used = 1; +- break; +- } +- } +- +- spin_unlock(&jz4740_dma_lock); +- +- if (!dma) +- return NULL; +- +- dma->dev = dev; +- dma->name = name; +- +- return dma; +-} +-EXPORT_SYMBOL_GPL(jz4740_dma_request); +- +-void jz4740_dma_configure(struct jz4740_dma_chan *dma, +- const struct jz4740_dma_config *config) +-{ +- uint32_t cmd; +- +- switch (config->transfer_size) { +- case JZ4740_DMA_TRANSFER_SIZE_2BYTE: +- dma->transfer_shift = 1; +- break; +- case JZ4740_DMA_TRANSFER_SIZE_4BYTE: +- dma->transfer_shift = 2; +- break; +- case JZ4740_DMA_TRANSFER_SIZE_16BYTE: +- dma->transfer_shift = 4; +- break; +- case JZ4740_DMA_TRANSFER_SIZE_32BYTE: +- dma->transfer_shift = 5; +- break; +- default: +- dma->transfer_shift = 0; +- break; +- } +- +- cmd = config->flags << JZ_DMA_CMD_FLAGS_OFFSET; +- cmd |= config->src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET; +- cmd |= config->dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET; +- cmd |= config->transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET; +- cmd |= config->mode << JZ_DMA_CMD_MODE_OFFSET; +- cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE; +- +- jz4740_dma_write(JZ_REG_DMA_CMD(dma->id), cmd); +- jz4740_dma_write(JZ_REG_DMA_STATUS_CTRL(dma->id), 0); +- jz4740_dma_write(JZ_REG_DMA_REQ_TYPE(dma->id), config->request_type); +-} +-EXPORT_SYMBOL_GPL(jz4740_dma_configure); +- +-void jz4740_dma_set_src_addr(struct jz4740_dma_chan *dma, dma_addr_t src) +-{ +- jz4740_dma_write(JZ_REG_DMA_SRC_ADDR(dma->id), src); +-} +-EXPORT_SYMBOL_GPL(jz4740_dma_set_src_addr); +- +-void jz4740_dma_set_dst_addr(struct jz4740_dma_chan *dma, dma_addr_t dst) +-{ +- jz4740_dma_write(JZ_REG_DMA_DST_ADDR(dma->id), dst); +-} +-EXPORT_SYMBOL_GPL(jz4740_dma_set_dst_addr); +- +-void jz4740_dma_set_transfer_count(struct jz4740_dma_chan *dma, uint32_t count) +-{ +- count >>= dma->transfer_shift; +- jz4740_dma_write(JZ_REG_DMA_TRANSFER_COUNT(dma->id), count); +-} +-EXPORT_SYMBOL_GPL(jz4740_dma_set_transfer_count); +- +-void jz4740_dma_set_complete_cb(struct jz4740_dma_chan *dma, +- jz4740_dma_complete_callback_t cb) +-{ +- dma->complete_cb = cb; +-} +-EXPORT_SYMBOL_GPL(jz4740_dma_set_complete_cb); +- +-void jz4740_dma_free(struct jz4740_dma_chan *dma) +-{ +- dma->dev = NULL; +- dma->complete_cb = NULL; +- dma->used = 0; +-} +-EXPORT_SYMBOL_GPL(jz4740_dma_free); +- +-void jz4740_dma_enable(struct jz4740_dma_chan *dma) +-{ +- jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), +- JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE, +- JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC | +- JZ_DMA_STATUS_CTRL_ENABLE); +- +- jz4740_dma_write_mask(JZ_REG_DMA_CTRL, +- JZ_DMA_CTRL_ENABLE, +- JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE); +-} +-EXPORT_SYMBOL_GPL(jz4740_dma_enable); +- +-void jz4740_dma_disable(struct jz4740_dma_chan *dma) +-{ +- jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0, +- JZ_DMA_STATUS_CTRL_ENABLE); +-} +-EXPORT_SYMBOL_GPL(jz4740_dma_disable); +- +-uint32_t jz4740_dma_get_residue(const struct jz4740_dma_chan *dma) +-{ +- uint32_t residue; +- residue = jz4740_dma_read(JZ_REG_DMA_TRANSFER_COUNT(dma->id)); +- return residue << dma->transfer_shift; +-} +-EXPORT_SYMBOL_GPL(jz4740_dma_get_residue); +- +-static void jz4740_dma_chan_irq(struct jz4740_dma_chan *dma) +-{ +- (void) jz4740_dma_read(JZ_REG_DMA_STATUS_CTRL(dma->id)); +- +- jz4740_dma_write_mask(JZ_REG_DMA_STATUS_CTRL(dma->id), 0, +- JZ_DMA_STATUS_CTRL_ENABLE | JZ_DMA_STATUS_CTRL_TRANSFER_DONE); +- +- if (dma->complete_cb) +- dma->complete_cb(dma, 0, dma->dev); +-} +- +-static irqreturn_t jz4740_dma_irq(int irq, void *dev_id) +-{ +- uint32_t irq_status; +- unsigned int i; +- +- irq_status = readl(jz4740_dma_base + JZ_REG_DMA_IRQ); +- +- for (i = 0; i < 6; ++i) { +- if (irq_status & (1 << i)) +- jz4740_dma_chan_irq(&jz4740_dma_channels[i]); +- } +- +- return IRQ_HANDLED; +-} +- +-static int jz4740_dma_init(void) +-{ +- struct clk *clk; +- unsigned int ret; +- +- jz4740_dma_base = ioremap(JZ4740_DMAC_BASE_ADDR, 0x400); +- +- if (!jz4740_dma_base) +- return -EBUSY; +- +- spin_lock_init(&jz4740_dma_lock); +- +- clk = clk_get(NULL, "dma"); +- if (IS_ERR(clk)) { +- ret = PTR_ERR(clk); +- printk(KERN_ERR "JZ4740 DMA: Failed to request clock: %d\n", +- ret); +- goto err_iounmap; +- } +- +- ret = request_irq(JZ4740_IRQ_DMAC, jz4740_dma_irq, 0, "DMA", NULL); +- if (ret) { +- printk(KERN_ERR "JZ4740 DMA: Failed to request irq: %d\n", ret); +- goto err_clkput; +- } +- +- clk_enable(clk); +- +- return 0; +- +-err_clkput: +- clk_put(clk); +- +-err_iounmap: +- iounmap(jz4740_dma_base); +- return ret; +-} +-arch_initcall(jz4740_dma_init); +diff --git a/drivers/dma/dma-jz4740.c b/drivers/dma/dma-jz4740.c +index 3d42434..b0c0c82 100644 +--- a/drivers/dma/dma-jz4740.c ++++ b/drivers/dma/dma-jz4740.c +@@ -22,6 +22,8 @@ + #include <linux/platform_device.h> + #include <linux/slab.h> + #include <linux/spinlock.h> ++#include <linux/irq.h> ++#include <linux/clk.h> + + #include <asm/mach-jz4740/dma.h> + +@@ -29,6 +31,76 @@ + + #define JZ_DMA_NR_CHANS 6 + ++#define JZ_REG_DMA_SRC_ADDR(x) (0x00 + (x) * 0x20) ++#define JZ_REG_DMA_DST_ADDR(x) (0x04 + (x) * 0x20) ++#define JZ_REG_DMA_TRANSFER_COUNT(x) (0x08 + (x) * 0x20) ++#define JZ_REG_DMA_REQ_TYPE(x) (0x0C + (x) * 0x20) ++#define JZ_REG_DMA_STATUS_CTRL(x) (0x10 + (x) * 0x20) ++#define JZ_REG_DMA_CMD(x) (0x14 + (x) * 0x20) ++#define JZ_REG_DMA_DESC_ADDR(x) (0x18 + (x) * 0x20) ++ ++#define JZ_REG_DMA_CTRL 0x300 ++#define JZ_REG_DMA_IRQ 0x304 ++#define JZ_REG_DMA_DOORBELL 0x308 ++#define JZ_REG_DMA_DOORBELL_SET 0x30C ++ ++#define JZ_DMA_STATUS_CTRL_NO_DESC BIT(31) ++#define JZ_DMA_STATUS_CTRL_DESC_INV BIT(6) ++#define JZ_DMA_STATUS_CTRL_ADDR_ERR BIT(4) ++#define JZ_DMA_STATUS_CTRL_TRANSFER_DONE BIT(3) ++#define JZ_DMA_STATUS_CTRL_HALT BIT(2) ++#define JZ_DMA_STATUS_CTRL_COUNT_TERMINATE BIT(1) ++#define JZ_DMA_STATUS_CTRL_ENABLE BIT(0) ++ ++#define JZ_DMA_CMD_SRC_INC BIT(23) ++#define JZ_DMA_CMD_DST_INC BIT(22) ++#define JZ_DMA_CMD_RDIL_MASK (0xf << 16) ++#define JZ_DMA_CMD_SRC_WIDTH_MASK (0x3 << 14) ++#define JZ_DMA_CMD_DST_WIDTH_MASK (0x3 << 12) ++#define JZ_DMA_CMD_INTERVAL_LENGTH_MASK (0x7 << 8) ++#define JZ_DMA_CMD_BLOCK_MODE BIT(7) ++#define JZ_DMA_CMD_DESC_VALID BIT(4) ++#define JZ_DMA_CMD_DESC_VALID_MODE BIT(3) ++#define JZ_DMA_CMD_VALID_IRQ_ENABLE BIT(2) ++#define JZ_DMA_CMD_TRANSFER_IRQ_ENABLE BIT(1) ++#define JZ_DMA_CMD_LINK_ENABLE BIT(0) ++ ++#define JZ_DMA_CMD_FLAGS_OFFSET 22 ++#define JZ_DMA_CMD_RDIL_OFFSET 16 ++#define JZ_DMA_CMD_SRC_WIDTH_OFFSET 14 ++#define JZ_DMA_CMD_DST_WIDTH_OFFSET 12 ++#define JZ_DMA_CMD_TRANSFER_SIZE_OFFSET 8 ++#define JZ_DMA_CMD_MODE_OFFSET 7 ++ ++#define JZ_DMA_CTRL_PRIORITY_MASK (0x3 << 8) ++#define JZ_DMA_CTRL_HALT BIT(3) ++#define JZ_DMA_CTRL_ADDRESS_ERROR BIT(2) ++#define JZ_DMA_CTRL_ENABLE BIT(0) ++ ++enum jz4740_dma_width { ++ JZ4740_DMA_WIDTH_32BIT = 0, ++ JZ4740_DMA_WIDTH_8BIT = 1, ++ JZ4740_DMA_WIDTH_16BIT = 2, ++}; ++ ++enum jz4740_dma_transfer_size { ++ JZ4740_DMA_TRANSFER_SIZE_4BYTE = 0, ++ JZ4740_DMA_TRANSFER_SIZE_1BYTE = 1, ++ JZ4740_DMA_TRANSFER_SIZE_2BYTE = 2, ++ JZ4740_DMA_TRANSFER_SIZE_16BYTE = 3, ++ JZ4740_DMA_TRANSFER_SIZE_32BYTE = 4, ++}; ++ ++enum jz4740_dma_flags { ++ JZ4740_DMA_SRC_AUTOINC = 0x2, ++ JZ4740_DMA_DST_AUTOINC = 0x1, ++}; ++ ++enum jz4740_dma_mode { ++ JZ4740_DMA_MODE_SINGLE = 0, ++ JZ4740_DMA_MODE_BLOCK = 1, ++}; ++ + struct jz4740_dma_sg { + dma_addr_t addr; + unsigned int len; +@@ -46,9 +118,10 @@ struct jz4740_dma_desc { + + struct jz4740_dmaengine_chan { + struct virt_dma_chan vchan; +- struct jz4740_dma_chan *jz_chan; ++ unsigned int id; + + dma_addr_t fifo_addr; ++ unsigned int transfer_shift; + + struct jz4740_dma_desc *desc; + unsigned int next_sg; +@@ -56,10 +129,19 @@ struct jz4740_dmaengine_chan { + + struct jz4740_dma_dev { + struct dma_device ddev; ++ void __iomem *base; ++ struct clk *clk; + + struct jz4740_dmaengine_chan chan[JZ_DMA_NR_CHANS]; + }; + ++static struct jz4740_dma_dev *jz4740_dma_chan_get_dev( ++ struct jz4740_dmaengine_chan *chan) ++{ ++ return container_of(chan->vchan.chan.device, struct jz4740_dma_dev, ++ ddev); ++} ++ + static struct jz4740_dmaengine_chan *to_jz4740_dma_chan(struct dma_chan *c) + { + return container_of(c, struct jz4740_dmaengine_chan, vchan.chan); +@@ -70,6 +152,29 @@ static struct jz4740_dma_desc *to_jz4740_dma_desc(struct virt_dma_desc *vdesc) + return container_of(vdesc, struct jz4740_dma_desc, vdesc); + } + ++static inline uint32_t jz4740_dma_read(struct jz4740_dma_dev *dmadev, ++ unsigned int reg) ++{ ++ return readl(dmadev->base + reg); ++} ++ ++static inline void jz4740_dma_write(struct jz4740_dma_dev *dmadev, ++ unsigned reg, uint32_t val) ++{ ++ writel(val, dmadev->base + reg); ++} ++ ++static inline void jz4740_dma_write_mask(struct jz4740_dma_dev *dmadev, ++ unsigned int reg, uint32_t val, uint32_t mask) ++{ ++ uint32_t tmp; ++ ++ tmp = jz4740_dma_read(dmadev, reg); ++ tmp &= ~mask; ++ tmp |= val; ++ jz4740_dma_write(dmadev, reg, tmp); ++} ++ + static struct jz4740_dma_desc *jz4740_dma_alloc_desc(unsigned int num_sgs) + { + return kzalloc(sizeof(struct jz4740_dma_desc) + +@@ -108,30 +213,60 @@ static int jz4740_dma_slave_config(struct dma_chan *c, + const struct dma_slave_config *config) + { + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); +- struct jz4740_dma_config jzcfg; ++ struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); ++ enum jz4740_dma_width src_width; ++ enum jz4740_dma_width dst_width; ++ enum jz4740_dma_transfer_size transfer_size; ++ enum jz4740_dma_flags flags; ++ uint32_t cmd; + + switch (config->direction) { + case DMA_MEM_TO_DEV: +- jzcfg.flags = JZ4740_DMA_SRC_AUTOINC; +- jzcfg.transfer_size = jz4740_dma_maxburst(config->dst_maxburst); ++ flags = JZ4740_DMA_SRC_AUTOINC; ++ transfer_size = jz4740_dma_maxburst(config->dst_maxburst); + chan->fifo_addr = config->dst_addr; + break; + case DMA_DEV_TO_MEM: +- jzcfg.flags = JZ4740_DMA_DST_AUTOINC; +- jzcfg.transfer_size = jz4740_dma_maxburst(config->src_maxburst); ++ flags = JZ4740_DMA_DST_AUTOINC; ++ transfer_size = jz4740_dma_maxburst(config->src_maxburst); + chan->fifo_addr = config->src_addr; + break; + default: + return -EINVAL; + } + ++ src_width = jz4740_dma_width(config->src_addr_width); ++ dst_width = jz4740_dma_width(config->dst_addr_width); ++ ++ switch (transfer_size) { ++ case JZ4740_DMA_TRANSFER_SIZE_2BYTE: ++ chan->transfer_shift = 1; ++ break; ++ case JZ4740_DMA_TRANSFER_SIZE_4BYTE: ++ chan->transfer_shift = 2; ++ break; ++ case JZ4740_DMA_TRANSFER_SIZE_16BYTE: ++ chan->transfer_shift = 4; ++ break; ++ case JZ4740_DMA_TRANSFER_SIZE_32BYTE: ++ chan->transfer_shift = 5; ++ break; ++ default: ++ chan->transfer_shift = 0; ++ break; ++ } + +- jzcfg.src_width = jz4740_dma_width(config->src_addr_width); +- jzcfg.dst_width = jz4740_dma_width(config->dst_addr_width); +- jzcfg.mode = JZ4740_DMA_MODE_SINGLE; +- jzcfg.request_type = config->slave_id; ++ cmd = flags << JZ_DMA_CMD_FLAGS_OFFSET; ++ cmd |= src_width << JZ_DMA_CMD_SRC_WIDTH_OFFSET; ++ cmd |= dst_width << JZ_DMA_CMD_DST_WIDTH_OFFSET; ++ cmd |= transfer_size << JZ_DMA_CMD_TRANSFER_SIZE_OFFSET; ++ cmd |= JZ4740_DMA_MODE_SINGLE << JZ_DMA_CMD_MODE_OFFSET; ++ cmd |= JZ_DMA_CMD_TRANSFER_IRQ_ENABLE; + +- jz4740_dma_configure(chan->jz_chan, &jzcfg); ++ jz4740_dma_write(dmadev, JZ_REG_DMA_CMD(chan->id), cmd); ++ jz4740_dma_write(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0); ++ jz4740_dma_write(dmadev, JZ_REG_DMA_REQ_TYPE(chan->id), ++ config->slave_id); + + return 0; + } +@@ -139,11 +274,13 @@ static int jz4740_dma_slave_config(struct dma_chan *c, + static int jz4740_dma_terminate_all(struct dma_chan *c) + { + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); ++ struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); + unsigned long flags; + LIST_HEAD(head); + + spin_lock_irqsave(&chan->vchan.lock, flags); +- jz4740_dma_disable(chan->jz_chan); ++ jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0, ++ JZ_DMA_STATUS_CTRL_ENABLE); + chan->desc = NULL; + vchan_get_all_descriptors(&chan->vchan, &head); + spin_unlock_irqrestore(&chan->vchan.lock, flags); +@@ -170,11 +307,13 @@ static int jz4740_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, + + static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan) + { ++ struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); + dma_addr_t src_addr, dst_addr; + struct virt_dma_desc *vdesc; + struct jz4740_dma_sg *sg; + +- jz4740_dma_disable(chan->jz_chan); ++ jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), 0, ++ JZ_DMA_STATUS_CTRL_ENABLE); + + if (!chan->desc) { + vdesc = vchan_next_desc(&chan->vchan); +@@ -196,22 +335,27 @@ static int jz4740_dma_start_transfer(struct jz4740_dmaengine_chan *chan) + src_addr = chan->fifo_addr; + dst_addr = sg->addr; + } +- jz4740_dma_set_src_addr(chan->jz_chan, src_addr); +- jz4740_dma_set_dst_addr(chan->jz_chan, dst_addr); +- jz4740_dma_set_transfer_count(chan->jz_chan, sg->len); ++ jz4740_dma_write(dmadev, JZ_REG_DMA_SRC_ADDR(chan->id), src_addr); ++ jz4740_dma_write(dmadev, JZ_REG_DMA_DST_ADDR(chan->id), dst_addr); ++ jz4740_dma_write(dmadev, JZ_REG_DMA_TRANSFER_COUNT(chan->id), ++ sg->len >> chan->transfer_shift); + + chan->next_sg++; + +- jz4740_dma_enable(chan->jz_chan); ++ jz4740_dma_write_mask(dmadev, JZ_REG_DMA_STATUS_CTRL(chan->id), ++ JZ_DMA_STATUS_CTRL_NO_DESC | JZ_DMA_STATUS_CTRL_ENABLE, ++ JZ_DMA_STATUS_CTRL_HALT | JZ_DMA_STATUS_CTRL_NO_DESC | ++ JZ_DMA_STATUS_CTRL_ENABLE); ++ ++ jz4740_dma_write_mask(dmadev, JZ_REG_DMA_CTRL, ++ JZ_DMA_CTRL_ENABLE, ++ JZ_DMA_CTRL_HALT | JZ_DMA_CTRL_ENABLE); + + return 0; + } + +-static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error, +- void *devid) ++static void jz4740_dma_chan_irq(struct jz4740_dmaengine_chan *chan) + { +- struct jz4740_dmaengine_chan *chan = devid; +- + spin_lock(&chan->vchan.lock); + if (chan->desc) { + if (chan->desc && chan->desc->cyclic) { +@@ -227,6 +371,28 @@ static void jz4740_dma_complete_cb(struct jz4740_dma_chan *jz_chan, int error, + spin_unlock(&chan->vchan.lock); + } + ++static irqreturn_t jz4740_dma_irq(int irq, void *devid) ++{ ++ struct jz4740_dma_dev *dmadev = devid; ++ uint32_t irq_status; ++ unsigned int i; ++ ++ irq_status = readl(dmadev->base + JZ_REG_DMA_IRQ); ++ ++ for (i = 0; i < 6; ++i) { ++ if (irq_status & (1 << i)) { ++ jz4740_dma_write_mask(dmadev, ++ JZ_REG_DMA_STATUS_CTRL(i), 0, ++ JZ_DMA_STATUS_CTRL_ENABLE | ++ JZ_DMA_STATUS_CTRL_TRANSFER_DONE); ++ ++ jz4740_dma_chan_irq(&dmadev->chan[i]); ++ } ++ } ++ ++ return IRQ_HANDLED; ++} ++ + static void jz4740_dma_issue_pending(struct dma_chan *c) + { + struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); +@@ -298,7 +464,8 @@ static struct dma_async_tx_descriptor *jz4740_dma_prep_dma_cyclic( + static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan, + struct jz4740_dma_desc *desc, unsigned int next_sg) + { +- size_t residue = 0; ++ struct jz4740_dma_dev *dmadev = jz4740_dma_chan_get_dev(chan); ++ unsigned int residue, count; + unsigned int i; + + residue = 0; +@@ -306,8 +473,11 @@ static size_t jz4740_dma_desc_residue(struct jz4740_dmaengine_chan *chan, + for (i = next_sg; i < desc->num_sgs; i++) + residue += desc->sg[i].len; + +- if (next_sg != 0) +- residue += jz4740_dma_get_residue(chan->jz_chan); ++ if (next_sg != 0) { ++ count = jz4740_dma_read(dmadev, ++ JZ_REG_DMA_TRANSFER_COUNT(chan->id)); ++ residue += count << chan->transfer_shift; ++ } + + return residue; + } +@@ -342,24 +512,12 @@ static enum dma_status jz4740_dma_tx_status(struct dma_chan *c, + + static int jz4740_dma_alloc_chan_resources(struct dma_chan *c) + { +- struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); +- +- chan->jz_chan = jz4740_dma_request(chan, NULL); +- if (!chan->jz_chan) +- return -EBUSY; +- +- jz4740_dma_set_complete_cb(chan->jz_chan, jz4740_dma_complete_cb); +- + return 0; + } + + static void jz4740_dma_free_chan_resources(struct dma_chan *c) + { +- struct jz4740_dmaengine_chan *chan = to_jz4740_dma_chan(c); +- +- vchan_free_chan_resources(&chan->vchan); +- jz4740_dma_free(chan->jz_chan); +- chan->jz_chan = NULL; ++ vchan_free_chan_resources(to_virt_chan(c)); + } + + static void jz4740_dma_desc_free(struct virt_dma_desc *vdesc) +@@ -373,7 +531,9 @@ static int jz4740_dma_probe(struct platform_device *pdev) + struct jz4740_dma_dev *dmadev; + struct dma_device *dd; + unsigned int i; ++ struct resource *res; + int ret; ++ int irq; + + dmadev = devm_kzalloc(&pdev->dev, sizeof(*dmadev), GFP_KERNEL); + if (!dmadev) +@@ -381,6 +541,17 @@ static int jz4740_dma_probe(struct platform_device *pdev) + + dd = &dmadev->ddev; + ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ dmadev->base = devm_ioremap_resource(&pdev->dev, res); ++ if (IS_ERR(dmadev->base)) ++ return PTR_ERR(dmadev->base); ++ ++ dmadev->clk = clk_get(&pdev->dev, "dma"); ++ if (IS_ERR(dmadev->clk)) ++ return PTR_ERR(dmadev->clk); ++ ++ clk_prepare_enable(dmadev->clk); ++ + dma_cap_set(DMA_SLAVE, dd->cap_mask); + dma_cap_set(DMA_CYCLIC, dd->cap_mask); + dd->device_alloc_chan_resources = jz4740_dma_alloc_chan_resources; +@@ -396,6 +567,7 @@ static int jz4740_dma_probe(struct platform_device *pdev) + + for (i = 0; i < dd->chancnt; i++) { + chan = &dmadev->chan[i]; ++ chan->id = i; + chan->vchan.desc_free = jz4740_dma_desc_free; + vchan_init(&chan->vchan, dd); + } +@@ -404,16 +576,28 @@ static int jz4740_dma_probe(struct platform_device *pdev) + if (ret) + return ret; + ++ irq = platform_get_irq(pdev, 0); ++ ret = request_irq(irq, jz4740_dma_irq, 0, dev_name(&pdev->dev), dmadev); ++ if (ret) ++ goto err_unregister; ++ + platform_set_drvdata(pdev, dmadev); + + return 0; ++ ++err_unregister: ++ dma_async_device_unregister(dd); ++ return ret; + } + + static int jz4740_dma_remove(struct platform_device *pdev) + { + struct jz4740_dma_dev *dmadev = platform_get_drvdata(pdev); ++ int irq = platform_get_irq(pdev, 0); + ++ free_irq(irq, dmadev); + dma_async_device_unregister(&dmadev->ddev); ++ clk_disable_unprepare(dmadev->clk); + + return 0; + } +-- +1.7.10.4 + diff --git a/target/linux/xburst/patches-3.10/016-ASoC-jz4740-Use-the-generic-dmaengine-PCM-driver.patch b/target/linux/xburst/patches-3.10/016-ASoC-jz4740-Use-the-generic-dmaengine-PCM-driver.patch new file mode 100644 index 0000000..296d5cc --- /dev/null +++ b/target/linux/xburst/patches-3.10/016-ASoC-jz4740-Use-the-generic-dmaengine-PCM-driver.patch @@ -0,0 +1,519 @@ +From a20f54fdd500e5bccc9bd1ca4ac9f150addf2e64 Mon Sep 17 00:00:00 2001 +From: Lars-Peter Clausen <lars@metafoo.de> +Date: Sat, 27 Apr 2013 21:26:30 +0200 +Subject: [PATCH 16/16] ASoC: jz4740: Use the generic dmaengine PCM driver + +Since there is a dmaengine driver for the jz4740 DMA controller now we can use +the generic dmaengine PCM driver instead of a custom one. + +Signed-off-by: Lars-Peter Clausen <lars@metafoo.de> +Acked-by: Mark Brown <broonie@opensource.wolfsonmicro.com> +--- + sound/soc/jz4740/Kconfig | 1 + + sound/soc/jz4740/jz4740-i2s.c | 48 +++---- + sound/soc/jz4740/jz4740-pcm.c | 310 ++--------------------------------------- + sound/soc/jz4740/jz4740-pcm.h | 20 --- + 4 files changed, 27 insertions(+), 352 deletions(-) + delete mode 100644 sound/soc/jz4740/jz4740-pcm.h + +diff --git a/sound/soc/jz4740/Kconfig b/sound/soc/jz4740/Kconfig +index 5351cba..29f76af 100644 +--- a/sound/soc/jz4740/Kconfig ++++ b/sound/soc/jz4740/Kconfig +@@ -1,6 +1,7 @@ + config SND_JZ4740_SOC + tristate "SoC Audio for Ingenic JZ4740 SoC" + depends on MACH_JZ4740 && SND_SOC ++ select SND_SOC_GENERIC_DMAENGINE_PCM + help + Say Y or M if you want to add support for codecs attached to + the JZ4740 I2S interface. You will also need to select the audio +diff --git a/sound/soc/jz4740/jz4740-i2s.c b/sound/soc/jz4740/jz4740-i2s.c +index 9a12644..5d04134 100644 +--- a/sound/soc/jz4740/jz4740-i2s.c ++++ b/sound/soc/jz4740/jz4740-i2s.c +@@ -29,9 +29,12 @@ + #include <sound/pcm_params.h> + #include <sound/soc.h> + #include <sound/initval.h> ++#include <sound/dmaengine_pcm.h> ++ ++#include <asm/mach-jz4740/dma.h> + + #include "jz4740-i2s.h" +-#include "jz4740-pcm.h" ++ + + #define JZ_REG_AIC_CONF 0x00 + #define JZ_REG_AIC_CTRL 0x04 +@@ -89,8 +92,8 @@ struct jz4740_i2s { + struct clk *clk_aic; + struct clk *clk_i2s; + +- struct jz4740_pcm_config pcm_config_playback; +- struct jz4740_pcm_config pcm_config_capture; ++ struct snd_dmaengine_dai_dma_data playback_dma_data; ++ struct snd_dmaengine_dai_dma_data capture_dma_data; + }; + + static inline uint32_t jz4740_i2s_read(const struct jz4740_i2s *i2s, +@@ -233,8 +236,6 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, struct snd_soc_dai *dai) + { + struct jz4740_i2s *i2s = snd_soc_dai_get_drvdata(dai); +- enum jz4740_dma_width dma_width; +- struct jz4740_pcm_config *pcm_config; + unsigned int sample_size; + uint32_t ctrl; + +@@ -243,11 +244,9 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S8: + sample_size = 0; +- dma_width = JZ4740_DMA_WIDTH_8BIT; + break; + case SNDRV_PCM_FORMAT_S16: + sample_size = 1; +- dma_width = JZ4740_DMA_WIDTH_16BIT; + break; + default: + return -EINVAL; +@@ -260,22 +259,13 @@ static int jz4740_i2s_hw_params(struct snd_pcm_substream *substream, + ctrl |= JZ_AIC_CTRL_MONO_TO_STEREO; + else + ctrl &= ~JZ_AIC_CTRL_MONO_TO_STEREO; +- +- pcm_config = &i2s->pcm_config_playback; +- pcm_config->dma_config.dst_width = dma_width; +- + } else { + ctrl &= ~JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_MASK; + ctrl |= sample_size << JZ_AIC_CTRL_INPUT_SAMPLE_SIZE_OFFSET; +- +- pcm_config = &i2s->pcm_config_capture; +- pcm_config->dma_config.src_width = dma_width; + } + + jz4740_i2s_write(i2s, JZ_REG_AIC_CTRL, ctrl); + +- snd_soc_dai_set_dma_data(dai, substream, pcm_config); +- + return 0; + } + +@@ -342,25 +332,19 @@ static int jz4740_i2s_resume(struct snd_soc_dai *dai) + + static void jz4740_i2c_init_pcm_config(struct jz4740_i2s *i2s) + { +- struct jz4740_dma_config *dma_config; ++ struct snd_dmaengine_dai_dma_data *dma_data; + + /* Playback */ +- dma_config = &i2s->pcm_config_playback.dma_config; +- 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; +- dma_config->mode = JZ4740_DMA_MODE_SINGLE; +- i2s->pcm_config_playback.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO; ++ dma_data = &i2s->playback_dma_data; ++ dma_data->maxburst = 16; ++ dma_data->slave_id = JZ4740_DMA_TYPE_AIC_TRANSMIT; ++ dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO; + + /* Capture */ +- dma_config = &i2s->pcm_config_capture.dma_config; +- 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; +- dma_config->mode = JZ4740_DMA_MODE_SINGLE; +- i2s->pcm_config_capture.fifo_addr = i2s->phys_base + JZ_REG_AIC_FIFO; ++ dma_data = &i2s->capture_dma_data; ++ dma_data->maxburst = 16; ++ dma_data->slave_id = JZ4740_DMA_TYPE_AIC_RECEIVE; ++ dma_data->addr = i2s->phys_base + JZ_REG_AIC_FIFO; + } + + static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) +@@ -371,6 +355,8 @@ static int jz4740_i2s_dai_probe(struct snd_soc_dai *dai) + clk_enable(i2s->clk_aic); + + jz4740_i2c_init_pcm_config(i2s); ++ dai->playback_dma_data = &i2s->playback_dma_data; ++ dai->capture_dma_data = &i2s->capture_dma_data; + + conf = (7 << JZ_AIC_CONF_FIFO_RX_THRESHOLD_OFFSET) | + (8 << JZ_AIC_CONF_FIFO_TX_THRESHOLD_OFFSET) | +diff --git a/sound/soc/jz4740/jz4740-pcm.c b/sound/soc/jz4740/jz4740-pcm.c +index 7100592..79fcade 100644 +--- a/sound/soc/jz4740/jz4740-pcm.c ++++ b/sound/soc/jz4740/jz4740-pcm.c +@@ -19,38 +19,14 @@ + #include <linux/platform_device.h> + #include <linux/slab.h> + +-#include <linux/dma-mapping.h> ++#include <sound/dmaengine_pcm.h> + +-#include <sound/core.h> +-#include <sound/pcm.h> +-#include <sound/pcm_params.h> +-#include <sound/soc.h> +- +-#include <asm/mach-jz4740/dma.h> +-#include "jz4740-pcm.h" +- +-struct jz4740_runtime_data { +- unsigned long dma_period; +- dma_addr_t dma_start; +- dma_addr_t dma_pos; +- dma_addr_t dma_end; +- +- struct jz4740_dma_chan *dma; +- +- dma_addr_t fifo_addr; +-}; +- +-/* identify hardware playback capabilities */ + static const struct snd_pcm_hardware jz4740_pcm_hardware = { + .info = SNDRV_PCM_INFO_MMAP | + SNDRV_PCM_INFO_MMAP_VALID | + SNDRV_PCM_INFO_INTERLEAVED | + SNDRV_PCM_INFO_BLOCK_TRANSFER, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S8, +- +- .rates = SNDRV_PCM_RATE_8000_48000, +- .channels_min = 1, +- .channels_max = 2, + .period_bytes_min = 16, + .period_bytes_max = 2 * PAGE_SIZE, + .periods_min = 2, +@@ -59,290 +35,22 @@ static const struct snd_pcm_hardware jz4740_pcm_hardware = { + .fifo_size = 32, + }; + +-static void jz4740_pcm_start_transfer(struct jz4740_runtime_data *prtd, +- struct snd_pcm_substream *substream) +-{ +- unsigned long count; +- +- if (prtd->dma_pos == prtd->dma_end) +- prtd->dma_pos = prtd->dma_start; +- +- if (prtd->dma_pos + prtd->dma_period > prtd->dma_end) +- count = prtd->dma_end - prtd->dma_pos; +- else +- count = prtd->dma_period; +- +- jz4740_dma_disable(prtd->dma); +- +- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { +- jz4740_dma_set_src_addr(prtd->dma, prtd->dma_pos); +- jz4740_dma_set_dst_addr(prtd->dma, prtd->fifo_addr); +- } else { +- jz4740_dma_set_src_addr(prtd->dma, prtd->fifo_addr); +- jz4740_dma_set_dst_addr(prtd->dma, prtd->dma_pos); +- } +- +- jz4740_dma_set_transfer_count(prtd->dma, count); +- +- prtd->dma_pos += count; +- +- jz4740_dma_enable(prtd->dma); +-} +- +-static void jz4740_pcm_dma_transfer_done(struct jz4740_dma_chan *dma, int err, +- void *dev_id) +-{ +- struct snd_pcm_substream *substream = dev_id; +- struct snd_pcm_runtime *runtime = substream->runtime; +- struct jz4740_runtime_data *prtd = runtime->private_data; +- +- snd_pcm_period_elapsed(substream); +- +- jz4740_pcm_start_transfer(prtd, substream); +-} +- +-static int jz4740_pcm_hw_params(struct snd_pcm_substream *substream, +- struct snd_pcm_hw_params *params) +-{ +- struct snd_pcm_runtime *runtime = substream->runtime; +- struct jz4740_runtime_data *prtd = runtime->private_data; +- struct snd_soc_pcm_runtime *rtd = substream->private_data; +- struct jz4740_pcm_config *config; +- +- config = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream); +- +- if (!config) +- return 0; +- +- if (!prtd->dma) { +- if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) +- prtd->dma = jz4740_dma_request(substream, "PCM Capture"); +- else +- prtd->dma = jz4740_dma_request(substream, "PCM Playback"); +- } +- +- if (!prtd->dma) +- return -EBUSY; +- +- jz4740_dma_configure(prtd->dma, &config->dma_config); +- prtd->fifo_addr = config->fifo_addr; +- +- jz4740_dma_set_complete_cb(prtd->dma, jz4740_pcm_dma_transfer_done); +- +- snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer); +- runtime->dma_bytes = params_buffer_bytes(params); +- +- prtd->dma_period = params_period_bytes(params); +- prtd->dma_start = runtime->dma_addr; +- prtd->dma_pos = prtd->dma_start; +- prtd->dma_end = prtd->dma_start + runtime->dma_bytes; +- +- return 0; +-} +- +-static int jz4740_pcm_hw_free(struct snd_pcm_substream *substream) +-{ +- struct jz4740_runtime_data *prtd = substream->runtime->private_data; +- +- snd_pcm_set_runtime_buffer(substream, NULL); +- if (prtd->dma) { +- jz4740_dma_free(prtd->dma); +- prtd->dma = NULL; +- } +- +- return 0; +-} +- +-static int jz4740_pcm_prepare(struct snd_pcm_substream *substream) +-{ +- struct jz4740_runtime_data *prtd = substream->runtime->private_data; +- +- if (!prtd->dma) +- return -EBUSY; +- +- prtd->dma_pos = prtd->dma_start; +- +- return 0; +-} +- +-static int jz4740_pcm_trigger(struct snd_pcm_substream *substream, int cmd) +-{ +- struct snd_pcm_runtime *runtime = substream->runtime; +- struct jz4740_runtime_data *prtd = runtime->private_data; +- +- switch (cmd) { +- case SNDRV_PCM_TRIGGER_START: +- case SNDRV_PCM_TRIGGER_RESUME: +- case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: +- jz4740_pcm_start_transfer(prtd, substream); +- break; +- case SNDRV_PCM_TRIGGER_STOP: +- case SNDRV_PCM_TRIGGER_SUSPEND: +- case SNDRV_PCM_TRIGGER_PAUSE_PUSH: +- jz4740_dma_disable(prtd->dma); +- break; +- default: +- break; +- } +- +- return 0; +-} +- +-static snd_pcm_uframes_t jz4740_pcm_pointer(struct snd_pcm_substream *substream) +-{ +- struct snd_pcm_runtime *runtime = substream->runtime; +- struct jz4740_runtime_data *prtd = runtime->private_data; +- unsigned long byte_offset; +- snd_pcm_uframes_t offset; +- struct jz4740_dma_chan *dma = prtd->dma; +- +- /* prtd->dma_pos points to the end of the current transfer. So by +- * subtracting prdt->dma_start we get the offset to the end of the +- * current period in bytes. By subtracting the residue of the transfer +- * we get the current offset in bytes. */ +- byte_offset = prtd->dma_pos - prtd->dma_start; +- byte_offset -= jz4740_dma_get_residue(dma); +- +- offset = bytes_to_frames(runtime, byte_offset); +- if (offset >= runtime->buffer_size) +- offset = 0; +- +- return offset; +-} +- +-static int jz4740_pcm_open(struct snd_pcm_substream *substream) +-{ +- struct snd_pcm_runtime *runtime = substream->runtime; +- struct jz4740_runtime_data *prtd; +- +- prtd = kzalloc(sizeof(*prtd), GFP_KERNEL); +- if (prtd == NULL) +- return -ENOMEM; +- +- snd_soc_set_runtime_hwparams(substream, &jz4740_pcm_hardware); +- +- runtime->private_data = prtd; +- +- return 0; +-} +- +-static int jz4740_pcm_close(struct snd_pcm_substream *substream) +-{ +- struct snd_pcm_runtime *runtime = substream->runtime; +- struct jz4740_runtime_data *prtd = runtime->private_data; +- +- kfree(prtd); +- +- return 0; +-} +- +-static int jz4740_pcm_mmap(struct snd_pcm_substream *substream, +- struct vm_area_struct *vma) +-{ +- return remap_pfn_range(vma, vma->vm_start, +- substream->dma_buffer.addr >> PAGE_SHIFT, +- vma->vm_end - vma->vm_start, vma->vm_page_prot); +-} +- +-static struct snd_pcm_ops jz4740_pcm_ops = { +- .open = jz4740_pcm_open, +- .close = jz4740_pcm_close, +- .ioctl = snd_pcm_lib_ioctl, +- .hw_params = jz4740_pcm_hw_params, +- .hw_free = jz4740_pcm_hw_free, +- .prepare = jz4740_pcm_prepare, +- .trigger = jz4740_pcm_trigger, +- .pointer = jz4740_pcm_pointer, +- .mmap = jz4740_pcm_mmap, +-}; +- +-static int jz4740_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream) +-{ +- struct snd_pcm_substream *substream = pcm->streams[stream].substream; +- struct snd_dma_buffer *buf = &substream->dma_buffer; +- size_t size = jz4740_pcm_hardware.buffer_bytes_max; +- +- buf->dev.type = SNDRV_DMA_TYPE_DEV; +- buf->dev.dev = pcm->card->dev; +- buf->private_data = NULL; +- +- buf->area = dma_alloc_noncoherent(pcm->card->dev, size, +- &buf->addr, GFP_KERNEL); +- if (!buf->area) +- return -ENOMEM; +- +- buf->bytes = size; +- +- return 0; +-} +- +-static void jz4740_pcm_free(struct snd_pcm *pcm) +-{ +- struct snd_pcm_substream *substream; +- struct snd_dma_buffer *buf; +- int stream; +- +- for (stream = 0; stream < SNDRV_PCM_STREAM_LAST; ++stream) { +- substream = pcm->streams[stream].substream; +- if (!substream) +- continue; +- +- buf = &substream->dma_buffer; +- if (!buf->area) +- continue; +- +- dma_free_noncoherent(pcm->card->dev, buf->bytes, buf->area, +- buf->addr); +- buf->area = NULL; +- } +-} +- +-static u64 jz4740_pcm_dmamask = DMA_BIT_MASK(32); +- +-static int jz4740_pcm_new(struct snd_soc_pcm_runtime *rtd) +-{ +- struct snd_card *card = rtd->card->snd_card; +- struct snd_pcm *pcm = rtd->pcm; +- int ret = 0; +- +- if (!card->dev->dma_mask) +- card->dev->dma_mask = &jz4740_pcm_dmamask; +- +- if (!card->dev->coherent_dma_mask) +- card->dev->coherent_dma_mask = DMA_BIT_MASK(32); +- +- if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) { +- ret = jz4740_pcm_preallocate_dma_buffer(pcm, +- SNDRV_PCM_STREAM_PLAYBACK); +- if (ret) +- goto err; +- } +- +- if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) { +- ret = jz4740_pcm_preallocate_dma_buffer(pcm, +- SNDRV_PCM_STREAM_CAPTURE); +- if (ret) +- goto err; +- } +- +-err: +- return ret; +-} +- +-static struct snd_soc_platform_driver jz4740_soc_platform = { +- .ops = &jz4740_pcm_ops, +- .pcm_new = jz4740_pcm_new, +- .pcm_free = jz4740_pcm_free, ++static const struct snd_dmaengine_pcm_config jz4740_dmaengine_pcm_config = { ++ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config, ++ .pcm_hardware = &jz4740_pcm_hardware, ++ .prealloc_buffer_size = 256 * PAGE_SIZE, + }; + + static int jz4740_pcm_probe(struct platform_device *pdev) + { +- return snd_soc_register_platform(&pdev->dev, &jz4740_soc_platform); ++ return snd_dmaengine_pcm_register(&pdev->dev, ++ &jz4740_dmaengine_pcm_config, ++ SND_DMAENGINE_PCM_FLAG_COMPAT); + } + + static int jz4740_pcm_remove(struct platform_device *pdev) + { +- snd_soc_unregister_platform(&pdev->dev); ++ snd_dmaengine_pcm_unregister(&pdev->dev); + return 0; + } + +diff --git a/sound/soc/jz4740/jz4740-pcm.h b/sound/soc/jz4740/jz4740-pcm.h +deleted file mode 100644 +index 1220cbb..0000000 +--- a/sound/soc/jz4740/jz4740-pcm.h ++++ /dev/null +@@ -1,20 +0,0 @@ +-/* +- * +- * 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. +- */ +- +-#ifndef _JZ4740_PCM_H +-#define _JZ4740_PCM_H +- +-#include <linux/dma-mapping.h> +-#include <asm/mach-jz4740/dma.h> +- +- +-struct jz4740_pcm_config { +- struct jz4740_dma_config dma_config; +- phys_addr_t fifo_addr; +-}; +- +-#endif +-- +1.7.10.4 + |