From 541158d5ad4a941d927a397737b6588d2e4db22d Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Jan 2010 04:44:45 +0000 Subject: Merge xburst target. SVN-Revision: 19098 --- .../xburst/files-2.6.32/drivers/char/defkeymap.c | 422 ++++ .../files-2.6.32/drivers/i2c/chips/n526-lpc.c | 237 ++ .../xburst/files-2.6.32/drivers/misc/jz4740-adc.c | 362 +++ .../xburst/files-2.6.32/drivers/mmc/host/jz_mmc.c | 955 ++++++++ .../files-2.6.32/drivers/mtd/nand/jz4740_nand.c | 418 ++++ .../files-2.6.32/drivers/power/jz4740-battery.c | 471 ++++ .../xburst/files-2.6.32/drivers/rtc/rtc-jz4740.c | 325 +++ .../files-2.6.32/drivers/usb/gadget/jz4740_udc.c | 2410 ++++++++++++++++++++ .../files-2.6.32/drivers/usb/gadget/jz4740_udc.h | 97 + .../drivers/video/backlight/gpm940b0.c | 255 +++ .../xburst/files-2.6.32/drivers/video/jz4740_fb.c | 575 +++++ 11 files changed, 6527 insertions(+) create mode 100644 target/linux/xburst/files-2.6.32/drivers/char/defkeymap.c create mode 100644 target/linux/xburst/files-2.6.32/drivers/i2c/chips/n526-lpc.c create mode 100644 target/linux/xburst/files-2.6.32/drivers/misc/jz4740-adc.c create mode 100644 target/linux/xburst/files-2.6.32/drivers/mmc/host/jz_mmc.c create mode 100644 target/linux/xburst/files-2.6.32/drivers/mtd/nand/jz4740_nand.c create mode 100644 target/linux/xburst/files-2.6.32/drivers/power/jz4740-battery.c create mode 100644 target/linux/xburst/files-2.6.32/drivers/rtc/rtc-jz4740.c create mode 100644 target/linux/xburst/files-2.6.32/drivers/usb/gadget/jz4740_udc.c create mode 100644 target/linux/xburst/files-2.6.32/drivers/usb/gadget/jz4740_udc.h create mode 100644 target/linux/xburst/files-2.6.32/drivers/video/backlight/gpm940b0.c create mode 100644 target/linux/xburst/files-2.6.32/drivers/video/jz4740_fb.c (limited to 'target/linux/xburst/files-2.6.32/drivers') diff --git a/target/linux/xburst/files-2.6.32/drivers/char/defkeymap.c b/target/linux/xburst/files-2.6.32/drivers/char/defkeymap.c new file mode 100644 index 0000000..fca8d59 --- /dev/null +++ b/target/linux/xburst/files-2.6.32/drivers/char/defkeymap.c @@ -0,0 +1,422 @@ +/* Do not edit this file! It was automatically generated by */ +/* loadkeys --mktable defkeymap.map > defkeymap.c */ + +#include +#include +#include + +u_short plain_map[NR_KEYS] = { + 0xf200, 0xf01b, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, 0xf036, + 0xf037, 0xf038, 0xf039, 0xf030, 0xf02d, 0xf03d, 0xf07f, 0xf009, + 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xfb75, 0xfb69, + 0xfb6f, 0xfb70, 0xf05b, 0xf05d, 0xf201, 0xf706, 0xfb61, 0xfb73, + 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xfb6a, 0xfb6b, 0xfb6c, 0xf03b, + 0xf027, 0xf060, 0xf700, 0xf05c, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, + 0xfb62, 0xfb6e, 0xfb6d, 0xf02c, 0xf02e, 0xf02f, 0xf701, 0xf30c, + 0xf703, 0xf020, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, + 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf209, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03c, 0xf10a, + 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short shift_map[NR_KEYS] = { + 0xf200, 0xf01b, 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, + 0xf026, 0xf02a, 0xf028, 0xf029, 0xf05f, 0xf02b, 0xf07f, 0xf009, + 0xfb51, 0xfb57, 0xfb45, 0xfb52, 0xfb54, 0xfb59, 0xfb55, 0xfb49, + 0xfb4f, 0xfb50, 0xf07b, 0xf07d, 0xf201, 0xf706, 0xfb41, 0xfb53, + 0xfb44, 0xfb46, 0xfb47, 0xfb48, 0xfb4a, 0xfb4b, 0xfb4c, 0xf03a, + 0xf022, 0xf07e, 0xf700, 0xf07c, 0xfb5a, 0xfb58, 0xfb43, 0xfb56, + 0xfb42, 0xfb4e, 0xfb4d, 0xf03b, 0xf03a, 0xf03f, 0xf701, 0xf30c, + 0xf703, 0xf020, 0xf207, 0xf10a, 0xf10b, 0xf10c, 0xf10d, 0xf10e, + 0xf10f, 0xf110, 0xf111, 0xf112, 0xf113, 0xf213, 0xf203, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf03e, 0xf10a, + 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf20b, 0xf601, 0xf602, 0xf117, 0xf600, 0xf20a, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short altgr_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf040, 0xf200, 0xf024, 0xf200, 0xf200, + 0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf05c, 0xf07e, 0xf008, 0xf200, + 0xf021, 0xf040, 0xf023, 0xf024, 0xf025, 0xf05e, 0xf026, 0xf02a, + 0xf028, 0xf029, 0xf200, 0xf07e, 0xf201, 0xf706, 0xf0b0, 0xf0a8, + 0xf0a4, 0xf02d, 0xf05f, 0xf07b, 0xf05b, 0xf05d, 0xf07d, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xf039, 0xf030, 0xf916, 0xfb76, + 0xf915, 0xf03c, 0xf03e, 0xf027, 0xf022, 0xf200, 0xf701, 0xf30c, + 0xf703, 0xf200, 0xf207, 0xf031, 0xf032, 0xf033, 0xf034, 0xf035, + 0xf036, 0xf037, 0xf038, 0xf514, 0xf515, 0xf208, 0xf202, 0xf911, + 0xf912, 0xf913, 0xf30b, 0xf90e, 0xf90f, 0xf910, 0xf30a, 0xf90b, + 0xf90c, 0xf90d, 0xf90a, 0xf310, 0xf206, 0xf200, 0xf07c, 0xf516, + 0xf517, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short ctrl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf000, 0xf01b, 0xf01c, 0xf01d, 0xf01e, + 0xf01f, 0xf07f, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, + 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, + 0xf00f, 0xf010, 0xf01b, 0xf01d, 0xf201, 0xf706, 0xf001, 0xf013, + 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, + 0xf007, 0xf000, 0xf700, 0xf01c, 0xf01a, 0xf018, 0xf003, 0xf016, + 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf20e, 0xf07f, 0xf701, 0xf30c, + 0xf703, 0xf000, 0xf207, 0xf100, 0xf101, 0xf102, 0xf103, 0xf104, + 0xf105, 0xf106, 0xf107, 0xf108, 0xf109, 0xf208, 0xf204, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf10a, + 0xf10b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short shift_ctrl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf000, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf01f, 0xf200, 0xf200, 0xf200, + 0xf011, 0xf017, 0xf005, 0xf012, 0xf014, 0xf019, 0xf015, 0xf009, + 0xf00f, 0xf010, 0xf200, 0xf200, 0xf201, 0xf706, 0xf001, 0xf013, + 0xf004, 0xf006, 0xf007, 0xf008, 0xf00a, 0xf00b, 0xf00c, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xf01a, 0xf018, 0xf003, 0xf016, + 0xf002, 0xf00e, 0xf00d, 0xf200, 0xf200, 0xf200, 0xf701, 0xf30c, + 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf208, 0xf200, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short alt_map[NR_KEYS] = { + 0xf200, 0xf81b, 0xf831, 0xf832, 0xf833, 0xf834, 0xf835, 0xf836, + 0xf837, 0xf838, 0xf839, 0xf830, 0xf82d, 0xf83d, 0xf87f, 0xf809, + 0xf871, 0xf877, 0xf865, 0xf872, 0xf874, 0xf879, 0xf875, 0xf869, + 0xf86f, 0xf870, 0xf85b, 0xf85d, 0xf80d, 0xf706, 0xf861, 0xf873, + 0xf864, 0xf866, 0xf867, 0xf868, 0xf86a, 0xf86b, 0xf86c, 0xf83b, + 0xf827, 0xf860, 0xf700, 0xf85c, 0xf87a, 0xf878, 0xf863, 0xf876, + 0xf862, 0xf86e, 0xf86d, 0xf200, 0xf200, 0xf82f, 0xf701, 0xf30c, + 0xf703, 0xf820, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, + 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf209, 0xf907, + 0xf908, 0xf909, 0xf30b, 0xf904, 0xf905, 0xf906, 0xf30a, 0xf901, + 0xf902, 0xf903, 0xf900, 0xf310, 0xf206, 0xf200, 0xf83c, 0xf50a, + 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf210, 0xf211, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short ctrl_alt_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf811, 0xf817, 0xf805, 0xf812, 0xf814, 0xf819, 0xf815, 0xf809, + 0xf80f, 0xf810, 0xf200, 0xf200, 0xf201, 0xf706, 0xf801, 0xf813, + 0xf804, 0xf806, 0xf807, 0xf808, 0xf80a, 0xf80b, 0xf80c, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xf81a, 0xf818, 0xf803, 0xf816, + 0xf802, 0xf80e, 0xf80d, 0xf200, 0xf200, 0xf200, 0xf701, 0xf30c, + 0xf703, 0xf200, 0xf207, 0xf500, 0xf501, 0xf502, 0xf503, 0xf504, + 0xf505, 0xf506, 0xf507, 0xf508, 0xf509, 0xf208, 0xf200, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf20c, 0xf206, 0xf200, 0xf200, 0xf50a, + 0xf50b, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf20c, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +static u_short ctl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf033, 0xf200, 0xf200, + 0xfb71, 0xfb77, 0xfb65, 0xfb72, 0xfb74, 0xfb79, 0xf037, 0xf038, + 0xf039, 0xfb70, 0xf200, 0xf200, 0xf201, 0xf706, 0xfb61, 0xfb73, + 0xfb64, 0xfb66, 0xfb67, 0xfb68, 0xf034, 0xf035, 0xf036, 0xf200, + 0xf200, 0xf200, 0xf700, 0xf200, 0xfb7a, 0xfb78, 0xfb63, 0xfb76, + 0xfb62, 0xf031, 0xf032, 0xf200, 0xf200, 0xf030, 0xf701, 0xf30c, + 0xf703, 0xf200, 0xf207, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf208, 0xf200, 0xf307, + 0xf308, 0xf309, 0xf30b, 0xf304, 0xf305, 0xf306, 0xf30a, 0xf301, + 0xf302, 0xf303, 0xf300, 0xf310, 0xf206, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf30e, 0xf702, 0xf30d, 0xf01c, 0xf701, 0xf205, 0xf114, 0xf603, + 0xf118, 0xf601, 0xf602, 0xf117, 0xf600, 0xf119, 0xf115, 0xf116, + 0xf11a, 0xf10c, 0xf10d, 0xf11b, 0xf11c, 0xf110, 0xf311, 0xf11d, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +ushort *key_maps[MAX_NR_KEYMAPS] = { + plain_map, shift_map, altgr_map, 0, + ctrl_map, shift_ctrl_map, 0, 0, + alt_map, 0, 0, 0, + ctrl_alt_map, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + 0, 0, 0, 0, + ctl_map, 0 +}; + +unsigned int keymap_count = 8; + +/* + * Philosophy: most people do not define more strings, but they who do + * often want quite a lot of string space. So, we statically allocate + * the default and allocate dynamically in chunks of 512 bytes. + */ + +char func_buf[] = { + '\033', '[', '[', 'A', 0, + '\033', '[', '[', 'B', 0, + '\033', '[', '[', 'C', 0, + '\033', '[', '[', 'D', 0, + '\033', '[', '[', 'E', 0, + '\033', '[', '1', '7', '~', 0, + '\033', '[', '1', '8', '~', 0, + '\033', '[', '1', '9', '~', 0, + '\033', '[', '2', '0', '~', 0, + '\033', '[', '2', '1', '~', 0, + '\033', '[', '2', '3', '~', 0, + '\033', '[', '2', '4', '~', 0, + '\033', '[', '2', '5', '~', 0, + '\033', '[', '2', '6', '~', 0, + '\033', '[', '2', '8', '~', 0, + '\033', '[', '2', '9', '~', 0, + '\033', '[', '3', '1', '~', 0, + '\033', '[', '3', '2', '~', 0, + '\033', '[', '3', '3', '~', 0, + '\033', '[', '3', '4', '~', 0, + '\033', '[', '1', '~', 0, + '\033', '[', '2', '~', 0, + '\033', '[', '3', '~', 0, + '\033', '[', '4', '~', 0, + '\033', '[', '5', '~', 0, + '\033', '[', '6', '~', 0, + '\033', '[', 'M', 0, + '\033', '[', 'P', 0, +}; + +char *funcbufptr = func_buf; +int funcbufsize = sizeof(func_buf); +int funcbufleft = 0; /* space left */ + +char *func_table[MAX_NR_FUNC] = { + func_buf + 0, + func_buf + 5, + func_buf + 10, + func_buf + 15, + func_buf + 20, + func_buf + 25, + func_buf + 31, + func_buf + 37, + func_buf + 43, + func_buf + 49, + func_buf + 55, + func_buf + 61, + func_buf + 67, + func_buf + 73, + func_buf + 79, + func_buf + 85, + func_buf + 91, + func_buf + 97, + func_buf + 103, + func_buf + 109, + func_buf + 115, + func_buf + 120, + func_buf + 125, + func_buf + 130, + func_buf + 135, + func_buf + 140, + func_buf + 145, + 0, + 0, + func_buf + 149, + 0, +}; + +struct kbdiacr accent_table[MAX_DIACR] = { + {'`', 'A', '\300'}, {'`', 'a', '\340'}, + {'\'', 'A', '\301'}, {'\'', 'a', '\341'}, + {'^', 'A', '\302'}, {'^', 'a', '\342'}, + {'~', 'A', '\303'}, {'~', 'a', '\343'}, + {'"', 'A', '\304'}, {'"', 'a', '\344'}, + {'O', 'A', '\305'}, {'o', 'a', '\345'}, + {'0', 'A', '\305'}, {'0', 'a', '\345'}, + {'A', 'A', '\305'}, {'a', 'a', '\345'}, + {'A', 'E', '\306'}, {'a', 'e', '\346'}, + {',', 'C', '\307'}, {',', 'c', '\347'}, + {'`', 'E', '\310'}, {'`', 'e', '\350'}, + {'\'', 'E', '\311'}, {'\'', 'e', '\351'}, + {'^', 'E', '\312'}, {'^', 'e', '\352'}, + {'"', 'E', '\313'}, {'"', 'e', '\353'}, + {'`', 'I', '\314'}, {'`', 'i', '\354'}, + {'\'', 'I', '\315'}, {'\'', 'i', '\355'}, + {'^', 'I', '\316'}, {'^', 'i', '\356'}, + {'"', 'I', '\317'}, {'"', 'i', '\357'}, + {'-', 'D', '\320'}, {'-', 'd', '\360'}, + {'~', 'N', '\321'}, {'~', 'n', '\361'}, + {'`', 'O', '\322'}, {'`', 'o', '\362'}, + {'\'', 'O', '\323'}, {'\'', 'o', '\363'}, + {'^', 'O', '\324'}, {'^', 'o', '\364'}, + {'~', 'O', '\325'}, {'~', 'o', '\365'}, + {'"', 'O', '\326'}, {'"', 'o', '\366'}, + {'/', 'O', '\330'}, {'/', 'o', '\370'}, + {'`', 'U', '\331'}, {'`', 'u', '\371'}, + {'\'', 'U', '\332'}, {'\'', 'u', '\372'}, + {'^', 'U', '\333'}, {'^', 'u', '\373'}, + {'"', 'U', '\334'}, {'"', 'u', '\374'}, + {'\'', 'Y', '\335'}, {'\'', 'y', '\375'}, + {'T', 'H', '\336'}, {'t', 'h', '\376'}, + {'s', 's', '\337'}, {'"', 'y', '\377'}, + {'s', 'z', '\337'}, {'i', 'j', '\377'}, +}; + +unsigned int accent_table_size = 68; diff --git a/target/linux/xburst/files-2.6.32/drivers/i2c/chips/n526-lpc.c b/target/linux/xburst/files-2.6.32/drivers/i2c/chips/n526-lpc.c new file mode 100644 index 0000000..4ac5c79 --- /dev/null +++ b/target/linux/xburst/files-2.6.32/drivers/i2c/chips/n526-lpc.c @@ -0,0 +1,237 @@ +/* + * Copyright (C) 2009, Lars-Peter Clausen + * + * 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. + * + * 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 +#include +#include +#include +#include +#include + +#include + +#include +#include + +struct n526_lpc { + struct i2c_client *client; + struct input_dev *input; + + struct work_struct work; +}; + +static const unsigned int n526_lpc_keymap[] = { + [0x01] = KEY_PAGEUP, + [0x02] = KEY_PAGEDOWN, + [0x03] = KEY_VOLUMEUP, + [0x04] = KEY_VOLUMEDOWN, + [0x06] = KEY_1, + [0x07] = KEY_Q, + [0x08] = KEY_A, + [0x09] = KEY_Z, + [0x0a] = KEY_LEFTSHIFT, + [0x0b] = KEY_2, + [0x0c] = KEY_W, + [0x0d] = KEY_S, + [0x0e] = KEY_X, + [0x0f] = KEY_REFRESH, + [0x10] = KEY_3, + [0x11] = KEY_E, + [0x12] = KEY_D, + [0x13] = KEY_C, + [0x14] = KEY_DOCUMENTS, + [0x15] = KEY_4, + [0x16] = KEY_R, + [0x17] = KEY_F, + [0x18] = KEY_V, + [0x19] = KEY_MUTE, + [0x1a] = KEY_5, + [0x1b] = KEY_T, + [0x1c] = KEY_G, + [0x1d] = KEY_B, + [0x1e] = KEY_DELETE, + [0x1f] = KEY_6, + [0x20] = KEY_Y, + [0x21] = KEY_H, + [0x22] = KEY_N, + [0x23] = KEY_SPACE, + [0x24] = KEY_7, + [0x25] = KEY_U, + [0x26] = KEY_J, + [0x27] = KEY_M, +/* [0x28] = KEY_SYM, */ + [0x29] = KEY_8, + [0x2a] = KEY_I, + [0x2b] = KEY_K, + [0x2c] = KEY_MENU, + [0x2d] = KEY_LEFT, + [0x2e] = KEY_9, + [0x2f] = KEY_O, + [0x30] = KEY_L, + [0x31] = KEY_UP, + [0x32] = KEY_DOWN, + [0x33] = KEY_0, + [0x34] = KEY_P, + [0x35] = KEY_BACKSPACE, + [0x36] = KEY_ENTER, + [0x37] = KEY_RIGHT, +}; + +static void n526_lpc_irq_work(struct work_struct *work) +{ + int ret; + struct n526_lpc *n526_lpc = container_of(work, struct n526_lpc, work); + struct i2c_client *client = n526_lpc->client; + unsigned char raw_msg; + struct i2c_msg msg = {client->addr, client->flags | I2C_M_RD, 1, &raw_msg}; + unsigned char keycode; + + + ret = i2c_transfer(client->adapter, &msg, 1); + + if (ret != 1) { + dev_err(&client->dev, "Failed to read lpc status\n"); + } + + keycode = raw_msg & 0x7f; + + if (keycode < ARRAY_SIZE(n526_lpc_keymap)) { + input_report_key(n526_lpc->input, n526_lpc_keymap[keycode], + !(raw_msg & 0x80)); + input_sync(n526_lpc->input); + } +} + +static irqreturn_t n526_lpc_irq(int irq, void *dev_id) +{ + struct n526_lpc *n526_lpc = dev_id; + + schedule_work(&n526_lpc->work); + return IRQ_HANDLED; +} + +static int __devinit n526_lpc_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int ret; + size_t i; + struct n526_lpc *n526_lpc; + struct input_dev *input; + + n526_lpc = kmalloc(sizeof(*n526_lpc), GFP_KERNEL); + + if (!n526_lpc) { + dev_err(&client->dev, "Failed to allocate device structure\n"); + return -ENOMEM; + } + + input = input_allocate_device(); + if (!input) { + dev_err(&client->dev, "Failed to allocate input device\n"); + ret = -ENOMEM; + goto err_free; + } + + input->name = "n526-keys"; + input->phys = "n526-keys/input0"; + input->dev.parent = &client->dev; + input->id.bustype = BUS_I2C; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0001; + + __set_bit(EV_KEY, input->evbit); + + for (i = 0; i < ARRAY_SIZE(n526_lpc_keymap); ++i) { + if (n526_lpc_keymap[i] != 0) + __set_bit(n526_lpc_keymap[i], input->keybit); + } + + ret = input_register_device(input); + + if (ret) { + dev_err(&client->dev, "Failed to register input device: %d\n", ret); + goto err_free_input; + } + + n526_lpc->client = client; + n526_lpc->input = input; + INIT_WORK(&n526_lpc->work, n526_lpc_irq_work); + + ret = request_irq(client->irq, n526_lpc_irq, IRQF_TRIGGER_FALLING, + "n526-lpc", n526_lpc); + if (ret) { + dev_err(&client->dev, "Failed to request irq: %d\n", ret); + goto err_unregister_input; + } + + i2c_set_clientdata(client, n526_lpc); + + return 0; + +err_unregister_input: + input_unregister_device(input); +err_free_input: + input_free_device(input); +err_free: + kfree(n526_lpc); + + return ret; +} + +static int n526_lpc_remove(struct i2c_client *client) +{ + struct n526_lpc *n526_lpc = i2c_get_clientdata(client); + + free_irq(client->irq, n526_lpc); + + i2c_set_clientdata(client, NULL); + input_unregister_device(n526_lpc->input); + input_free_device(n526_lpc->input); + kfree(n526_lpc); + + return 0; +} + +static const struct i2c_device_id n526_lpc_id[] = { + { "n526-lpc", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, n526_lpc_id); + +static struct i2c_driver n526_lpc_driver = { + .driver = { + .name = "n526-lpc", + .owner = THIS_MODULE, + }, + .probe = n526_lpc_probe, + .remove = n526_lpc_remove, + .id_table = n526_lpc_id, +}; + +static int __init n526_lpc_init(void) +{ + return i2c_add_driver(&n526_lpc_driver); +} +module_init(n526_lpc_init); + +static void __exit n526_lpc_exit(void) +{ + i2c_del_driver(&n526_lpc_driver); +} +module_exit(n526_lpc_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lars-Peter Clausen"); +MODULE_DESCRIPTION("n526 keypad driver"); +MODULE_ALIAS("i2c:n526-keys"); diff --git a/target/linux/xburst/files-2.6.32/drivers/misc/jz4740-adc.c b/target/linux/xburst/files-2.6.32/drivers/misc/jz4740-adc.c new file mode 100644 index 0000000..7da3bcc --- /dev/null +++ b/target/linux/xburst/files-2.6.32/drivers/misc/jz4740-adc.c @@ -0,0 +1,362 @@ +/* + * Copyright (C) 2009, Lars-Peter Clausen + * JZ4720/JZ4740 SoC ADC driver + * + * 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. + * + * This driver is meant to synchronize access to the adc core for the battery + * and touchscreen driver. Thus these drivers should use the adc driver as a + * parent. + */ + +#include +#include +#include +#include +#include +#include + +#define JZ_REG_ADC_ENABLE 0x00 +#define JZ_REG_ADC_CFG 0x04 +#define JZ_REG_ADC_CTRL 0x08 +#define JZ_REG_ADC_STATUS 0x0C +#define JZ_REG_ADC_SAME 0x10 +#define JZ_REG_ADC_WAIT 0x14 +#define JZ_REG_ADC_TOUCH 0x18 +#define JZ_REG_ADC_BATTERY 0x1C +#define JZ_REG_ADC_ADCIN 0x20 + +#define JZ_ADC_ENABLE_TOUCH BIT(2) +#define JZ_ADC_ENABLE_BATTERY BIT(1) +#define JZ_ADC_ENABLE_ADCIN BIT(0) + +#define JZ_ADC_CFG_SPZZ BIT(31) +#define JZ_ADC_CFG_EX_IN BIT(30) +#define JZ_ADC_CFG_DNUM_MASK (0x7 << 16) +#define JZ_ADC_CFG_DMA_ENABLE BIT(15) +#define JZ_ADC_CFG_XYZ_MASK (0x2 << 13) +#define JZ_ADC_CFG_SAMPLE_NUM_MASK (0x7 << 10) +#define JZ_ADC_CFG_CLKDIV (0xf << 5) +#define JZ_ADC_CFG_BAT_MB BIT(4) + +#define JZ_ADC_CFG_DNUM_OFFSET 16 +#define JZ_ADC_CFG_XYZ_OFFSET 13 +#define JZ_ADC_CFG_SAMPLE_NUM_OFFSET 10 +#define JZ_ADC_CFG_CLKDIV_OFFSET 5 + +#define JZ_ADC_IRQ_PENDOWN BIT(4) +#define JZ_ADC_IRQ_PENUP BIT(3) +#define JZ_ADC_IRQ_TOUCH BIT(2) +#define JZ_ADC_IRQ_BATTERY BIT(1) +#define JZ_ADC_IRQ_ADCIN BIT(0) + +#define JZ_ADC_TOUCH_TYPE1 BIT(31) +#define JZ_ADC_TOUCH_DATA1_MASK 0xfff +#define JZ_ADC_TOUCH_TYPE0 BIT(15) +#define JZ_ADC_TOUCH_DATA0_MASK 0xfff + +#define JZ_ADC_BATTERY_MASK 0xfff + +#define JZ_ADC_ADCIN_MASK 0xfff + +struct jz4740_adc { + struct resource *mem; + void __iomem *base; + + int irq; + + struct completion bat_completion; + struct completion adc_completion; + + spinlock_t lock; +}; + +static irqreturn_t jz4740_adc_irq(int irq, void *data) +{ + struct jz4740_adc *adc = data; + uint8_t status; + + status = readb(adc->base + JZ_REG_ADC_STATUS); + + if (status & JZ_ADC_IRQ_BATTERY) + complete(&adc->bat_completion); + if (status & JZ_ADC_IRQ_ADCIN) + complete(&adc->adc_completion); + + writeb(0xff, adc->base + JZ_REG_ADC_STATUS); + + return IRQ_HANDLED; +} + +static void jz4740_adc_enable_irq(struct jz4740_adc *adc, int irq) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&adc->lock, flags); + + val = readb(adc->base + JZ_REG_ADC_CTRL); + val &= ~irq; + writeb(val, adc->base + JZ_REG_ADC_CTRL); + + spin_unlock_irqrestore(&adc->lock, flags); +} + +static void jz4740_adc_disable_irq(struct jz4740_adc *adc, int irq) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&adc->lock, flags); + + val = readb(adc->base + JZ_REG_ADC_CTRL); + val |= irq; + writeb(val, adc->base + JZ_REG_ADC_CTRL); + + spin_unlock_irqrestore(&adc->lock, flags); +} + +static void jz4740_adc_enable_adc(struct jz4740_adc *adc, int engine) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&adc->lock, flags); + + val = readb(adc->base + JZ_REG_ADC_ENABLE); + val |= engine; + writeb(val, adc->base + JZ_REG_ADC_ENABLE); + + spin_unlock_irqrestore(&adc->lock, flags); +} + +static void jz4740_adc_disable_adc(struct jz4740_adc *adc, int engine) +{ + unsigned long flags; + uint8_t val; + + spin_lock_irqsave(&adc->lock, flags); + + val = readb(adc->base + JZ_REG_ADC_ENABLE); + val &= ~engine; + writeb(val, adc->base + JZ_REG_ADC_ENABLE); + + spin_unlock_irqrestore(&adc->lock, flags); +} + +static inline void jz4740_adc_set_cfg(struct jz4740_adc *adc, uint32_t mask, +uint32_t val) +{ + unsigned long flags; + uint32_t cfg; + + spin_lock_irqsave(&adc->lock, flags); + + cfg = readl(adc->base + JZ_REG_ADC_CFG); + + cfg &= ~mask; + cfg |= val; + + writel(cfg, adc->base + JZ_REG_ADC_CFG); + + spin_unlock_irqrestore(&adc->lock, flags); +} + +long jz4740_adc_read_battery_voltage(struct device *dev, + enum jz_adc_battery_scale scale) +{ + struct jz4740_adc *adc = dev_get_drvdata(dev); + unsigned long t; + long long voltage; + uint16_t val; + + if (!adc) + return -ENODEV; + + if (scale == JZ_ADC_BATTERY_SCALE_2V5) + jz4740_adc_set_cfg(adc, JZ_ADC_CFG_BAT_MB, JZ_ADC_CFG_BAT_MB); + else + jz4740_adc_set_cfg(adc, JZ_ADC_CFG_BAT_MB, 0); + + jz4740_adc_enable_irq(adc, JZ_ADC_IRQ_BATTERY); + jz4740_adc_enable_adc(adc, JZ_ADC_ENABLE_BATTERY); + + t = wait_for_completion_interruptible_timeout(&adc->bat_completion, + HZ); + + jz4740_adc_disable_irq(adc, JZ_ADC_IRQ_BATTERY); + + if (t <= 0) { + jz4740_adc_disable_adc(adc, JZ_ADC_ENABLE_BATTERY); + return t ? t : -ETIMEDOUT; + } + + val = readw(adc->base + JZ_REG_ADC_BATTERY); + + if (scale == JZ_ADC_BATTERY_SCALE_2V5) + voltage = (((long long)val) * 2500000LL) >> 12LL; + else + voltage = ((((long long)val) * 7395000LL) >> 12LL) + 33000LL; + + return voltage; +} +EXPORT_SYMBOL_GPL(jz4740_adc_read_battery_voltage); + +static ssize_t jz4740_adc_read_adcin(struct device *dev, + struct device_attribute *dev_attr, + char *buf) +{ + struct jz4740_adc *adc = dev_get_drvdata(dev); + unsigned long t; + uint16_t val; + + jz4740_adc_enable_irq(adc, JZ_ADC_IRQ_ADCIN); + jz4740_adc_enable_adc(adc, JZ_ADC_ENABLE_ADCIN); + + t = wait_for_completion_interruptible_timeout(&adc->adc_completion, + HZ); + + jz4740_adc_disable_irq(adc, JZ_ADC_IRQ_ADCIN); + + if (t <= 0) { + jz4740_adc_disable_adc(adc, JZ_ADC_ENABLE_ADCIN); + return t ? t : -ETIMEDOUT; + } + + val = readw(adc->base + JZ_REG_ADC_ADCIN); + + return sprintf(buf, "%d\n", val); +} + +static DEVICE_ATTR(adcin, S_IRUGO, jz4740_adc_read_adcin, NULL); + +static int __devinit jz4740_adc_probe(struct platform_device *pdev) +{ + int ret; + struct jz4740_adc *adc; + + adc = kmalloc(sizeof(*adc), GFP_KERNEL); + + adc->irq = platform_get_irq(pdev, 0); + + if (adc->irq < 0) { + ret = adc->irq; + dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); + goto err_free; + } + + adc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!adc->mem) { + ret = -ENOENT; + dev_err(&pdev->dev, "Failed to get platform mmio resource\n"); + goto err_free; + } + + adc->mem = request_mem_region(adc->mem->start, resource_size(adc->mem), + pdev->name); + + if (!adc->mem) { + ret = -EBUSY; + dev_err(&pdev->dev, "Failed to request mmio memory region\n"); + goto err_free; + } + + adc->base = ioremap_nocache(adc->mem->start, resource_size(adc->mem)); + + if (!adc->base) { + ret = -EBUSY; + dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); + goto err_release_mem_region; + } + + + init_completion(&adc->bat_completion); + init_completion(&adc->adc_completion); + + spin_lock_init(&adc->lock); + + platform_set_drvdata(pdev, adc); + + ret = request_irq(adc->irq, jz4740_adc_irq, 0, pdev->name, adc); + + if (ret) { + dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); + goto err_iounmap; + } + + ret = device_create_file(&pdev->dev, &dev_attr_adcin); + if (ret) { + dev_err(&pdev->dev, "Failed to create sysfs file: %d\n", ret); + goto err_free_irq; + } + + writeb(0x00, adc->base + JZ_REG_ADC_ENABLE); + writeb(0xff, adc->base + JZ_REG_ADC_CTRL); + + return 0; + +err_free_irq: + free_irq(adc->irq, adc); +err_iounmap: + platform_set_drvdata(pdev, NULL); + iounmap(adc->base); +err_release_mem_region: + release_mem_region(adc->mem->start, resource_size(adc->mem)); +err_free: + kfree(adc); + + return ret; +} + +static int __devexit jz4740_adc_remove(struct platform_device *pdev) +{ + struct jz4740_adc *adc = platform_get_drvdata(pdev); + + device_remove_file(&pdev->dev, &dev_attr_adcin); + + free_irq(adc->irq, adc); + + iounmap(adc->base); + release_mem_region(adc->mem->start, resource_size(adc->mem)); + + platform_set_drvdata(pdev, NULL); + + kfree(adc); + + return 0; +} + +struct platform_driver jz4740_adc_driver = { + .probe = jz4740_adc_probe, + .remove = jz4740_adc_remove, + .driver = { + .name = "jz4740-adc", + .owner = THIS_MODULE, + }, +}; + +static int __init jz4740_adc_init(void) +{ + return platform_driver_register(&jz4740_adc_driver); +} +module_init(jz4740_adc_init); + +static void __exit jz4740_adc_exit(void) +{ + platform_driver_unregister(&jz4740_adc_driver); +} +module_exit(jz4740_adc_exit); + +MODULE_DESCRIPTION("JZ4720/JZ4740 SoC ADC driver"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:jz4740-adc"); +MODULE_ALIAS("platform:jz4720-adc"); diff --git a/target/linux/xburst/files-2.6.32/drivers/mmc/host/jz_mmc.c b/target/linux/xburst/files-2.6.32/drivers/mmc/host/jz_mmc.c new file mode 100644 index 0000000..e4bdde0 --- /dev/null +++ b/target/linux/xburst/files-2.6.32/drivers/mmc/host/jz_mmc.c @@ -0,0 +1,955 @@ +/* + * Copyright (C) 2009, Lars-Peter Clausen + * JZ7420/JZ4740 GPIO SD/MMC controller driver + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#define JZ_REG_MMC_STRPCL 0x00 +#define JZ_REG_MMC_STATUS 0x04 +#define JZ_REG_MMC_CLKRT 0x08 +#define JZ_REG_MMC_CMDAT 0x0C +#define JZ_REG_MMC_RESTO 0x10 +#define JZ_REG_MMC_RDTO 0x14 +#define JZ_REG_MMC_BLKLEN 0x18 +#define JZ_REG_MMC_NOB 0x1C +#define JZ_REG_MMC_SNOB 0x20 +#define JZ_REG_MMC_IMASK 0x24 +#define JZ_REG_MMC_IREG 0x28 +#define JZ_REG_MMC_CMD 0x2C +#define JZ_REG_MMC_ARG 0x30 +#define JZ_REG_MMC_RESP_FIFO 0x34 +#define JZ_REG_MMC_RXFIFO 0x38 +#define JZ_REG_MMC_TXFIFO 0x3C + +#define JZ_MMC_STRPCL_EXIT_MULTIPLE BIT(7) +#define JZ_MMC_STRPCL_EXIT_TRANSFER BIT(6) +#define JZ_MMC_STRPCL_START_READWAIT BIT(5) +#define JZ_MMC_STRPCL_STOP_READWAIT BIT(4) +#define JZ_MMC_STRPCL_RESET BIT(3) +#define JZ_MMC_STRPCL_START_OP BIT(2) +#define JZ_MMC_STRPCL_CLOCK_CONTROL BIT(1) | BIT(0) +#define JZ_MMC_STRPCL_CLOCK_STOP BIT(0) +#define JZ_MMC_STRPCL_CLOCK_START BIT(1) + + +#define JZ_MMC_STATUS_IS_RESETTING BIT(15) +#define JZ_MMC_STATUS_SDIO_INT_ACTIVE BIT(14) +#define JZ_MMC_STATUS_PRG_DONE BIT(13) +#define JZ_MMC_STATUS_DATA_TRAN_DONE BIT(12) +#define JZ_MMC_STATUS_END_CMD_RES BIT(11) +#define JZ_MMC_STATUS_DATA_FIFO_AFULL BIT(10) +#define JZ_MMC_STATUS_IS_READWAIT BIT(9) +#define JZ_MMC_STATUS_CLK_EN BIT(8) +#define JZ_MMC_STATUS_DATA_FIFO_FULL BIT(7) +#define JZ_MMC_STATUS_DATA_FIFO_EMPTY BIT(6) +#define JZ_MMC_STATUS_CRC_RES_ERR BIT(5) +#define JZ_MMC_STATUS_CRC_READ_ERROR BIT(4) +#define JZ_MMC_STATUS_TIMEOUT_WRITE BIT(3) +#define JZ_MMC_STATUS_CRC_WRITE_ERROR BIT(2) +#define JZ_MMC_STATUS_TIMEOUT_RES BIT(1) +#define JZ_MMC_STATUS_TIMEOUT_READ BIT(0) + +#define JZ_MMC_STATUS_READ_ERROR_MASK (BIT(4) | BIT(0)) +#define JZ_MMC_STATUS_WRITE_ERROR_MASK (BIT(3) | BIT(2)) + + +#define JZ_MMC_CMDAT_IO_ABORT BIT(11) +#define JZ_MMC_CMDAT_BUS_WIDTH_4BIT BIT(10) +#define JZ_MMC_CMDAT_DMA_EN BIT(8) +#define JZ_MMC_CMDAT_INIT BIT(7) +#define JZ_MMC_CMDAT_BUSY BIT(6) +#define JZ_MMC_CMDAT_STREAM BIT(5) +#define JZ_MMC_CMDAT_WRITE BIT(4) +#define JZ_MMC_CMDAT_DATA_EN BIT(3) +#define JZ_MMC_CMDAT_RESPONSE_FORMAT BIT(2) | BIT(1) | BIT(0) +#define JZ_MMC_CMDAT_RSP_R1 1 +#define JZ_MMC_CMDAT_RSP_R2 2 +#define JZ_MMC_CMDAT_RSP_R3 3 + +#define JZ_MMC_IRQ_SDIO BIT(7) +#define JZ_MMC_IRQ_TXFIFO_WR_REQ BIT(6) +#define JZ_MMC_IRQ_RXFIFO_RD_REQ BIT(5) +#define JZ_MMC_IRQ_END_CMD_RES BIT(2) +#define JZ_MMC_IRQ_PRG_DONE BIT(1) +#define JZ_MMC_IRQ_DATA_TRAN_DONE BIT(0) + + +#define JZ_MMC_CLK_RATE 24000000 + +struct jz4740_mmc_host { + struct mmc_host *mmc; + struct platform_device *pdev; + struct jz4740_mmc_platform_data *pdata; + struct clk *clk; + + int irq; + int card_detect_irq; + + struct resource *mem; + void __iomem *base; + struct mmc_request *req; + struct mmc_command *cmd; + + int max_clock; + uint32_t cmdat; + + uint16_t irq_mask; + + spinlock_t lock; + struct timer_list clock_timer; + struct timer_list timeout_timer; + unsigned waiting:1; +}; + +static void jz4740_mmc_cmd_done(struct jz4740_mmc_host *host); + +static void jz4740_mmc_enable_irq(struct jz4740_mmc_host *host, unsigned int irq) +{ + unsigned long flags; + spin_lock_irqsave(&host->lock, flags); + + host->irq_mask &= ~irq; + writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK); + + spin_unlock_irqrestore(&host->lock, flags); +} + +static void jz4740_mmc_disable_irq(struct jz4740_mmc_host *host, unsigned int irq) +{ + unsigned long flags; + spin_lock_irqsave(&host->lock, flags); + + host->irq_mask |= irq; + writew(host->irq_mask, host->base + JZ_REG_MMC_IMASK); + + spin_unlock_irqrestore(&host->lock, flags); +} + +static void jz4740_mmc_clock_enable(struct jz4740_mmc_host *host, bool start_transfer) +{ + uint16_t val = JZ_MMC_STRPCL_CLOCK_START; + + if (start_transfer) + val |= JZ_MMC_STRPCL_START_OP; + + writew(val, host->base + JZ_REG_MMC_STRPCL); +} + +static void jz4740_mmc_clock_disable(struct jz4740_mmc_host *host) +{ + uint16_t status; + writew(JZ_MMC_STRPCL_CLOCK_STOP, host->base + JZ_REG_MMC_STRPCL); + do { + status = readl(host->base + JZ_REG_MMC_STATUS); + } while (status & JZ_MMC_STATUS_CLK_EN); + +} + +static void jz4740_mmc_reset(struct jz4740_mmc_host *host) +{ + writew(JZ_MMC_STRPCL_RESET, host->base + JZ_REG_MMC_STRPCL); + udelay(10); + while(readw(host->base + JZ_REG_MMC_STATUS) & JZ_MMC_STATUS_IS_RESETTING); +} + +static void jz4740_mmc_request_done(struct jz4740_mmc_host *host) +{ + struct mmc_request *req; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + req = host->req; + host->req = NULL; + host->waiting = 0; + spin_unlock_irqrestore(&host->lock, flags); + + if (!unlikely(req)) + return; + +/* if (req->cmd->error != 0) { + printk("error\n"); + jz4740_mmc_reset(host); + }*/ + + mmc_request_done(host->mmc, req); +} + +static void jz4740_mmc_write_data(struct jz4740_mmc_host *host, struct mmc_data *data) { + struct scatterlist *sg; + uint32_t *sg_pointer; + int status; + size_t i, j; + + for (sg = data->sg; sg; sg = sg_next(sg)) { + sg_pointer = sg_virt(sg); + i = sg->length / 4; + j = i >> 3; + i = i & 0x7; + while (j) { + do { + status = readw(host->base + JZ_REG_MMC_IREG); + } while (!(status & JZ_MMC_IRQ_TXFIFO_WR_REQ)); + writew(JZ_MMC_IRQ_TXFIFO_WR_REQ, host->base + JZ_REG_MMC_IREG); + + writel(sg_pointer[0], host->base + JZ_REG_MMC_TXFIFO); + writel(sg_pointer[1], host->base + JZ_REG_MMC_TXFIFO); + writel(sg_pointer[2], host->base + JZ_REG_MMC_TXFIFO); + writel(sg_pointer[3], host->base + JZ_REG_MMC_TXFIFO); + writel(sg_pointer[4], host->base + JZ_REG_MMC_TXFIFO); + writel(sg_pointer[5], host->base + JZ_REG_MMC_TXFIFO); + writel(sg_pointer[6], host->base + JZ_REG_MMC_TXFIFO); + writel(sg_pointer[7], host->base + JZ_REG_MMC_TXFIFO); + sg_pointer += 8; + --j; + } + if (i) { + do { + status = readw(host->base + JZ_REG_MMC_IREG); + } while (!(status & JZ_MMC_IRQ_TXFIFO_WR_REQ)); + writew(JZ_MMC_IRQ_TXFIFO_WR_REQ, host->base + JZ_REG_MMC_IREG); + + while (i) { + writel(*sg_pointer, host->base + JZ_REG_MMC_TXFIFO); + ++sg_pointer; + --i; + } + } + data->bytes_xfered += sg->length; + } + + status = readl(host->base + JZ_REG_MMC_STATUS); + if (status & JZ_MMC_STATUS_WRITE_ERROR_MASK) + goto err; + + writew(JZ_MMC_IRQ_TXFIFO_WR_REQ, host->base + JZ_REG_MMC_IREG); + do { + status = readl(host->base + JZ_REG_MMC_STATUS); + } while ((status & JZ_MMC_STATUS_DATA_TRAN_DONE) == 0); + writew(JZ_MMC_IRQ_DATA_TRAN_DONE, host->base + JZ_REG_MMC_IREG); + + return; +err: + if(status & (JZ_MMC_STATUS_TIMEOUT_WRITE)) { + host->req->cmd->error = -ETIMEDOUT; + data->error = -ETIMEDOUT; + } else { + host->req->cmd->error = -EILSEQ; + data->error = -EILSEQ; + } +} + +static void jz4740_mmc_timeout(unsigned long data) +{ + struct jz4740_mmc_host *host = (struct jz4740_mmc_host*)data; + unsigned long flags; + + spin_lock_irqsave(&host->lock, flags); + if (!host->waiting) { + spin_unlock_irqrestore(&host->lock, flags); + return; + } + + host->waiting = 0; + + spin_unlock_irqrestore(&host->lock, flags); + + host->req->cmd->error = -ETIMEDOUT; + jz4740_mmc_request_done(host); +} + +static void jz4740_mmc_read_data(struct jz4740_mmc_host *host, struct mmc_data *data) { + struct scatterlist *sg; + uint32_t *sg_pointer; + uint32_t d; + uint16_t status = 0; + size_t i, j; + + for (sg = data->sg; sg; sg = sg_next(sg)) { + sg_pointer = sg_virt(sg); + i = sg->length; + j = i >> 5; + i = i & 0x1f; + while (j) { + do { + status = readw(host->base + JZ_REG_MMC_IREG); + } while (!(status & JZ_MMC_IRQ_RXFIFO_RD_REQ)); + writew(JZ_MMC_IRQ_RXFIFO_RD_REQ, host->base + JZ_REG_MMC_IREG); + + sg_pointer[0] = readl(host->base + JZ_REG_MMC_RXFIFO); + sg_pointer[1] = readl(host->base + JZ_REG_MMC_RXFIFO); + sg_pointer[2] = readl(host->base + JZ_REG_MMC_RXFIFO); + sg_pointer[3] = readl(host->base + JZ_REG_MMC_RXFIFO); + sg_pointer[4] = readl(host->base + JZ_REG_MMC_RXFIFO); + sg_pointer[5] = readl(host->base + JZ_REG_MMC_RXFIFO); + sg_pointer[6] = readl(host->base + JZ_REG_MMC_RXFIFO); + sg_pointer[7] = readl(host->base + JZ_REG_MMC_RXFIFO); + + sg_pointer += 8; + --j; + } + + while (i >= 4) { + do { + status = readl(host->base + JZ_REG_MMC_STATUS); + } while ((status & JZ_MMC_STATUS_DATA_FIFO_EMPTY)); + + *sg_pointer = readl(host->base + JZ_REG_MMC_RXFIFO); + ++sg_pointer; + i -= 4; + } + if (i > 0) { + d = readl(host->base + JZ_REG_MMC_RXFIFO); + memcpy(sg_pointer, &d, i); + } + data->bytes_xfered += sg->length; + + flush_dcache_page(sg_page(sg)); + } + + status = readl(host->base + JZ_REG_MMC_STATUS); + if (status & JZ_MMC_STATUS_READ_ERROR_MASK) + goto err; + + /* For whatever reason there is sometime one word more in the fifo then + * requested */ + while ((status & JZ_MMC_STATUS_DATA_FIFO_EMPTY) == 0) { + d = readl(host->base + JZ_REG_MMC_RXFIFO); + status = readl(host->base + JZ_REG_MMC_STATUS); + } + return; + +err: + if(status & JZ_MMC_STATUS_TIMEOUT_READ) { + host->req->cmd->error = -ETIMEDOUT; + data->error = -ETIMEDOUT; + } else { + host->req->cmd->error = -EILSEQ; + data->error = -EILSEQ; + } +} + +static irqreturn_t jz_mmc_irq_worker(int irq, void *devid) +{ + struct jz4740_mmc_host *host = (struct jz4740_mmc_host*)devid; + + if (host->cmd->error) + jz4740_mmc_request_done(host); + else + jz4740_mmc_cmd_done(host); + + return IRQ_HANDLED; +} + +static irqreturn_t jz_mmc_irq(int irq, void *devid) +{ + struct jz4740_mmc_host *host = devid; + uint16_t irq_reg, status, tmp; + unsigned long flags; + irqreturn_t ret = IRQ_HANDLED; + + irq_reg = readw(host->base + JZ_REG_MMC_IREG); + + tmp = irq_reg; + spin_lock(&host->lock); + irq_reg &= ~host->irq_mask; + spin_unlock(&host->lock); + + if (irq_reg & JZ_MMC_IRQ_SDIO) { + writew(JZ_MMC_IRQ_SDIO, host->base + JZ_REG_MMC_IREG); + mmc_signal_sdio_irq(host->mmc); + } + + if (!host->req || !host->cmd) { + goto handled; + } + + + spin_lock_irqsave(&host->lock, flags); + if (!host->waiting) { + spin_unlock_irqrestore(&host->lock, flags); + goto handled; + } + host->waiting = 0; + spin_unlock_irqrestore(&host->lock, flags); + + del_timer(&host->timeout_timer); + + status = readl(host->base + JZ_REG_MMC_STATUS); + + if (status & JZ_MMC_STATUS_TIMEOUT_RES) { + host->cmd->error = -ETIMEDOUT; + } else if (status & JZ_MMC_STATUS_CRC_RES_ERR) { + host->cmd->error = -EIO; + } else if(status & (JZ_MMC_STATUS_CRC_READ_ERROR | + JZ_MMC_STATUS_CRC_WRITE_ERROR)) { + host->cmd->data->error = -EIO; + } else if(status & (JZ_MMC_STATUS_CRC_READ_ERROR | + JZ_MMC_STATUS_CRC_WRITE_ERROR)) { + host->cmd->data->error = -EIO; + } + + if (irq_reg & JZ_MMC_IRQ_END_CMD_RES) { + jz4740_mmc_disable_irq(host, JZ_MMC_IRQ_END_CMD_RES); + writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG); + ret = IRQ_WAKE_THREAD; + } + + return ret; +handled: + + writew(0xff, host->base + JZ_REG_MMC_IREG); + return IRQ_HANDLED; +} + +static int jz4740_mmc_set_clock_rate(struct jz4740_mmc_host *host, int rate) { + int div = 0; + int real_rate = host->max_clock; + jz4740_mmc_clock_disable(host); + + while ((real_rate >> 1) >= rate && div < 7) { + ++div; + real_rate >>= 1; + } + clk_set_rate(host->clk, JZ_MMC_CLK_RATE); + + writew(div, host->base + JZ_REG_MMC_CLKRT); + return real_rate; +} + + +static void jz4740_mmc_read_response(struct jz4740_mmc_host *host, struct mmc_command *cmd) +{ + int i; + uint16_t tmp; + if (cmd->flags & MMC_RSP_136) { + tmp = readw(host->base + JZ_REG_MMC_RESP_FIFO); + for (i = 0; i < 4; ++i) { + cmd->resp[i] = tmp << 24; + cmd->resp[i] |= readw(host->base + JZ_REG_MMC_RESP_FIFO) << 8; + tmp = readw(host->base + JZ_REG_MMC_RESP_FIFO); + cmd->resp[i] |= tmp >> 8; + } + } else { + cmd->resp[0] = readw(host->base + JZ_REG_MMC_RESP_FIFO) << 24; + cmd->resp[0] |= readw(host->base + JZ_REG_MMC_RESP_FIFO) << 8; + cmd->resp[0] |= readw(host->base + JZ_REG_MMC_RESP_FIFO) & 0xff; + } +} + +static void jz4740_mmc_send_command(struct jz4740_mmc_host *host, struct mmc_command *cmd) +{ + uint32_t cmdat = host->cmdat; + + host->cmdat &= ~JZ_MMC_CMDAT_INIT; + jz4740_mmc_clock_disable(host); + + host->cmd = cmd; + + if (cmd->flags & MMC_RSP_BUSY) + cmdat |= JZ_MMC_CMDAT_BUSY; + + switch (mmc_resp_type(cmd)) { + case MMC_RSP_R1B: + case MMC_RSP_R1: + cmdat |= JZ_MMC_CMDAT_RSP_R1; + break; + case MMC_RSP_R2: + cmdat |= JZ_MMC_CMDAT_RSP_R2; + break; + case MMC_RSP_R3: + cmdat |= JZ_MMC_CMDAT_RSP_R3; + break; + default: + break; + } + + if (cmd->data) { + cmdat |= JZ_MMC_CMDAT_DATA_EN; + if (cmd->data->flags & MMC_DATA_WRITE) + cmdat |= JZ_MMC_CMDAT_WRITE; + if (cmd->data->flags & MMC_DATA_STREAM) + cmdat |= JZ_MMC_CMDAT_STREAM; + + writew(cmd->data->blksz, host->base + JZ_REG_MMC_BLKLEN); + writew(cmd->data->blocks, host->base + JZ_REG_MMC_NOB); + } + + writeb(cmd->opcode, host->base + JZ_REG_MMC_CMD); + writel(cmd->arg, host->base + JZ_REG_MMC_ARG); + writel(cmdat, host->base + JZ_REG_MMC_CMDAT); + + host->waiting = 1; + jz4740_mmc_clock_enable(host, 1); + mod_timer(&host->timeout_timer, 4*HZ); +} + +static void jz4740_mmc_cmd_done(struct jz4740_mmc_host *host) +{ + uint32_t status; + struct mmc_command *cmd = host->req->cmd; + struct mmc_request *req = host->req; + status = readl(host->base + JZ_REG_MMC_STATUS); + + if (cmd->flags & MMC_RSP_PRESENT) + jz4740_mmc_read_response(host, cmd); + + if (cmd->data) { + if (cmd->data->flags & MMC_DATA_READ) + jz4740_mmc_read_data(host, cmd->data); + else + jz4740_mmc_write_data(host, cmd->data); + } + + if (req->stop) { + jz4740_mmc_send_command(host, req->stop); + do { + status = readl(host->base + JZ_REG_MMC_STATUS); + } while ((status & JZ_MMC_STATUS_PRG_DONE) == 0); + writew(JZ_MMC_IRQ_PRG_DONE, host->base + JZ_REG_MMC_IREG); + } + + jz4740_mmc_request_done(host); +} + +static void jz4740_mmc_request(struct mmc_host *mmc, struct mmc_request *req) +{ + struct jz4740_mmc_host *host = mmc_priv(mmc); + + host->req = req; + + writew(0xffff, host->base + JZ_REG_MMC_IREG); + + writew(JZ_MMC_IRQ_END_CMD_RES, host->base + JZ_REG_MMC_IREG); + jz4740_mmc_enable_irq(host, JZ_MMC_IRQ_END_CMD_RES); + jz4740_mmc_send_command(host, req->cmd); +} + + +static void jz4740_mmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +{ + struct jz4740_mmc_host *host = mmc_priv(mmc); + if (ios->clock) + jz4740_mmc_set_clock_rate(host, ios->clock); + + switch(ios->power_mode) { + case MMC_POWER_UP: + if (gpio_is_valid(host->pdata->gpio_power)) + gpio_set_value(host->pdata->gpio_power, + !host->pdata->power_active_low); + host->cmdat |= JZ_MMC_CMDAT_INIT; + clk_enable(host->clk); + break; + case MMC_POWER_ON: + break; + default: + if (gpio_is_valid(host->pdata->gpio_power)) + gpio_set_value(host->pdata->gpio_power, + host->pdata->power_active_low); + clk_disable(host->clk); + break; + } + + switch(ios->bus_width) { + case MMC_BUS_WIDTH_1: + host->cmdat &= ~JZ_MMC_CMDAT_BUS_WIDTH_4BIT; + break; + case MMC_BUS_WIDTH_4: + host->cmdat |= JZ_MMC_CMDAT_BUS_WIDTH_4BIT; + break; + default: + dev_err(&host->pdev->dev, "Invalid bus width: %d\n", ios->bus_width); + } +} + +static int jz4740_mmc_get_ro(struct mmc_host *mmc) +{ + struct jz4740_mmc_host *host = mmc_priv(mmc); + if (!gpio_is_valid(host->pdata->gpio_read_only)) + return -ENOSYS; + + return gpio_get_value(host->pdata->gpio_read_only) ^ + host->pdata->read_only_active_low; +} + +static int jz4740_mmc_get_cd(struct mmc_host *mmc) +{ + struct jz4740_mmc_host *host = mmc_priv(mmc); + if (!gpio_is_valid(host->pdata->gpio_card_detect)) + return -ENOSYS; + + return gpio_get_value(host->pdata->gpio_card_detect) ^ + host->pdata->card_detect_active_low; +} + +static irqreturn_t jz4740_mmc_card_detect_irq(int irq, void *devid) +{ + struct jz4740_mmc_host *host = devid; + + mmc_detect_change(host->mmc, HZ / 3); + + return IRQ_HANDLED; +} + +static void jz4740_mmc_enable_sdio_irq(struct mmc_host *mmc, int enable) +{ + struct jz4740_mmc_host *host = mmc_priv(mmc); + if (enable) + jz4740_mmc_enable_irq(host, JZ_MMC_IRQ_SDIO); + else + jz4740_mmc_disable_irq(host, JZ_MMC_IRQ_SDIO); +} + +static const struct mmc_host_ops jz4740_mmc_ops = { + .request = jz4740_mmc_request, + .set_ios = jz4740_mmc_set_ios, + .get_ro = jz4740_mmc_get_ro, + .get_cd = jz4740_mmc_get_cd, + .enable_sdio_irq = jz4740_mmc_enable_sdio_irq, +}; + +static const struct jz_gpio_bulk_request jz4740_mmc_pins[] = { + JZ_GPIO_BULK_PIN(MSC_CMD), + JZ_GPIO_BULK_PIN(MSC_CLK), + JZ_GPIO_BULK_PIN(MSC_DATA0), + JZ_GPIO_BULK_PIN(MSC_DATA1), + JZ_GPIO_BULK_PIN(MSC_DATA2), + JZ_GPIO_BULK_PIN(MSC_DATA3), +}; + +static int __devinit jz4740_mmc_request_gpios(struct platform_device *pdev) +{ + int ret; + struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata) + return 0; + + if (gpio_is_valid(pdata->gpio_card_detect)) { + ret = gpio_request(pdata->gpio_card_detect, "MMC detect change"); + if (ret) { + dev_err(&pdev->dev, "Failed to request detect change gpio\n"); + goto err; + } + gpio_direction_input(pdata->gpio_card_detect); + } + + if (gpio_is_valid(pdata->gpio_read_only)) { + ret = gpio_request(pdata->gpio_read_only, "MMC read only"); + if (ret) { + dev_err(&pdev->dev, "Failed to request read only gpio: %d\n", ret); + goto err_free_gpio_card_detect; + } + gpio_direction_input(pdata->gpio_read_only); + } + + if (gpio_is_valid(pdata->gpio_power)) { + ret = gpio_request(pdata->gpio_power, "MMC power"); + if (ret) { + dev_err(&pdev->dev, "Failed to request power gpio: %d\n", ret); + goto err_free_gpio_read_only; + } + gpio_direction_output(pdata->gpio_power, pdata->power_active_low); + } + + return 0; + +err_free_gpio_read_only: + if (gpio_is_valid(pdata->gpio_read_only)) + gpio_free(pdata->gpio_read_only); +err_free_gpio_card_detect: + if (gpio_is_valid(pdata->gpio_card_detect)) + gpio_free(pdata->gpio_card_detect); +err: + return ret; +} + +static void jz4740_mmc_free_gpios(struct platform_device *pdev) +{ + struct jz4740_mmc_platform_data *pdata = pdev->dev.platform_data; + + if (!pdata) + return; + + if (gpio_is_valid(pdata->gpio_power)) + gpio_free(pdata->gpio_power); + if (gpio_is_valid(pdata->gpio_read_only)) + gpio_free(pdata->gpio_read_only); + if (gpio_is_valid(pdata->gpio_card_detect)) + gpio_free(pdata->gpio_card_detect); +} + +static int __devinit jz4740_mmc_probe(struct platform_device* pdev) +{ + int ret; + struct mmc_host *mmc; + struct jz4740_mmc_host *host; + struct jz4740_mmc_platform_data *pdata; + + pdata = pdev->dev.platform_data; + + mmc = mmc_alloc_host(sizeof(struct jz4740_mmc_host), &pdev->dev); + + if (!mmc) { + dev_err(&pdev->dev, "Failed to alloc mmc host structure\n"); + return -ENOMEM; + } + + host = mmc_priv(mmc); + + host->irq = platform_get_irq(pdev, 0); + + if (host->irq < 0) { + ret = host->irq; + dev_err(&pdev->dev, "Failed to get platform irq: %d\n", ret); + goto err_free_host; + } + + host->clk = clk_get(&pdev->dev, "mmc"); + if (!host->clk) { + ret = -ENOENT; + dev_err(&pdev->dev, "Failed to get mmc clock\n"); + goto err_free_host; + } + + host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!host->mem) { + ret = -ENOENT; + dev_err(&pdev->dev, "Failed to get base platform memory\n"); + goto err_clk_put; + } + + host->mem = request_mem_region(host->mem->start, resource_size(host->mem), + pdev->name); + + if (!host->mem) { + ret = -EBUSY; + dev_err(&pdev->dev, "Failed to request base memory region\n"); + goto err_clk_put; + } + + host->base = ioremap_nocache(host->mem->start, resource_size(host->mem)); + + if (!host->base) { + ret = -EBUSY; + dev_err(&pdev->dev, "Failed to ioremap base memory\n"); + goto err_release_mem_region; + } + + if (pdata && pdata->data_1bit) + ret = jz_gpio_bulk_request(jz4740_mmc_pins, ARRAY_SIZE(jz4740_mmc_pins) - 3); + else + ret = jz_gpio_bulk_request(jz4740_mmc_pins, ARRAY_SIZE(jz4740_mmc_pins)); + + if (ret) { + dev_err(&pdev->dev, "Failed to request function pins: %d\n", ret); + goto err_iounmap; + } + + ret = jz4740_mmc_request_gpios(pdev); + if (ret) + goto err_gpio_bulk_free; + + mmc->ops = &jz4740_mmc_ops; + mmc->f_min = JZ_MMC_CLK_RATE / 128; + mmc->f_max = JZ_MMC_CLK_RATE; + mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; + mmc->caps = (pdata && pdata->data_1bit) ? 0 : MMC_CAP_4_BIT_DATA; + mmc->caps |= MMC_CAP_SDIO_IRQ; + mmc->max_seg_size = 4096; + mmc->max_phys_segs = 128; + + mmc->max_blk_size = (1 << 10) - 1; + mmc->max_blk_count = (1 << 15) - 1; + mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count; + + host->mmc = mmc; + host->pdev = pdev; + host->pdata = pdata; + host->max_clock = JZ_MMC_CLK_RATE; + spin_lock_init(&host->lock); + host->irq_mask = 0xffff; + + host->card_detect_irq = gpio_to_irq(pdata->gpio_card_detect); + + if (host->card_detect_irq < 0) { + dev_warn(&pdev->dev, "Failed to get irq for card detect gpio\n"); + } else { + ret = request_irq(host->card_detect_irq, + jz4740_mmc_card_detect_irq, IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, "MMC/SD detect changed", host); + + if (ret) { + dev_err(&pdev->dev, "Failed to request card detect irq"); + goto err_free_gpios; + } + } + + ret = request_threaded_irq(host->irq, jz_mmc_irq, jz_mmc_irq_worker, IRQF_DISABLED, "MMC/SD", host); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); + goto err_free_card_detect_irq; + } + + jz4740_mmc_reset(host); + jz4740_mmc_clock_disable(host); + setup_timer(&host->timeout_timer, jz4740_mmc_timeout, (unsigned long)host); + + platform_set_drvdata(pdev, host); + ret = mmc_add_host(mmc); + + if (ret) { + dev_err(&pdev->dev, "Failed to add mmc host: %d\n", ret); + goto err_free_irq; + } + printk("JZ SD/MMC card driver registered\n"); + + return 0; + +err_free_irq: + free_irq(host->irq, host); +err_free_card_detect_irq: + if (host->card_detect_irq >= 0) + free_irq(host->card_detect_irq, host); +err_free_gpios: + jz4740_mmc_free_gpios(pdev); +err_gpio_bulk_free: + if (pdata && pdata->data_1bit) + jz_gpio_bulk_free(jz4740_mmc_pins, ARRAY_SIZE(jz4740_mmc_pins) - 3); + else + jz_gpio_bulk_free(jz4740_mmc_pins, ARRAY_SIZE(jz4740_mmc_pins)); +err_iounmap: + iounmap(host->base); +err_release_mem_region: + release_mem_region(host->mem->start, resource_size(host->mem)); +err_clk_put: + clk_put(host->clk); +err_free_host: + platform_set_drvdata(pdev, NULL); + mmc_free_host(mmc); + + return ret; +} + +static int jz4740_mmc_remove(struct platform_device *pdev) +{ + struct jz4740_mmc_host *host = platform_get_drvdata(pdev); + struct jz4740_mmc_platform_data *pdata = host->pdata; + + del_timer_sync(&host->timeout_timer); + jz4740_mmc_disable_irq(host, 0xff); + jz4740_mmc_reset(host); + + mmc_remove_host(host->mmc); + + free_irq(host->irq, host); + if (host->card_detect_irq >= 0) + free_irq(host->card_detect_irq, host); + + jz4740_mmc_free_gpios(pdev); + if (pdata && pdata->data_1bit) + jz_gpio_bulk_free(jz4740_mmc_pins, ARRAY_SIZE(jz4740_mmc_pins) - 3); + else + jz_gpio_bulk_free(jz4740_mmc_pins, ARRAY_SIZE(jz4740_mmc_pins)); + + iounmap(host->base); + release_mem_region(host->mem->start, resource_size(host->mem)); + + clk_put(host->clk); + + platform_set_drvdata(pdev, NULL); + mmc_free_host(host->mmc); + + return 0; +} + +#ifdef CONFIG_PM +static int jz4740_mmc_suspend(struct device *dev) +{ + struct jz4740_mmc_host *host = dev_get_drvdata(dev); + struct jz4740_mmc_platform_data *pdata = host->pdata; + + mmc_suspend_host(host->mmc, PMSG_SUSPEND); + + if (pdata && pdata->data_1bit) + jz_gpio_bulk_suspend(jz4740_mmc_pins, ARRAY_SIZE(jz4740_mmc_pins) - 3); + else + jz_gpio_bulk_suspend(jz4740_mmc_pins, ARRAY_SIZE(jz4740_mmc_pins)); + + return 0; +} + +static int jz4740_mmc_resume(struct device *dev) +{ + struct jz4740_mmc_host *host = dev_get_drvdata(dev); + struct jz4740_mmc_platform_data *pdata = host->pdata; + + if (pdata && pdata->data_1bit) + jz_gpio_bulk_resume(jz4740_mmc_pins, ARRAY_SIZE(jz4740_mmc_pins) - 3); + else + jz_gpio_bulk_resume(jz4740_mmc_pins, ARRAY_SIZE(jz4740_mmc_pins)); + + mmc_resume_host(host->mmc); + + return 0; +} + +struct dev_pm_ops jz4740_mmc_pm_ops = { + .suspend = jz4740_mmc_suspend, + .resume = jz4740_mmc_resume, + .poweroff = jz4740_mmc_suspend, + .restore = jz4740_mmc_resume, +}; + +#define jz4740_mmc_PM_OPS (&jz4740_mmc_pm_ops) +#else +#define jz4740_mmc_PM_OPS NULL +#endif + +static struct platform_driver jz4740_mmc_driver = { + .probe = jz4740_mmc_probe, + .remove = jz4740_mmc_remove, + .driver = { + .name = "jz4740-mmc", + .owner = THIS_MODULE, + .pm = jz4740_mmc_PM_OPS, + }, +}; + +static int __init jz4740_mmc_init(void) { + return platform_driver_register(&jz4740_mmc_driver); +} +module_init(jz4740_mmc_init); + +static void __exit jz4740_mmc_exit(void) { + platform_driver_unregister(&jz4740_mmc_driver); +} +module_exit(jz4740_mmc_exit); + +MODULE_DESCRIPTION("JZ4720/JZ4740 SD/MMC controller driver"); +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lars-Peter Clausen "); diff --git a/target/linux/xburst/files-2.6.32/drivers/mtd/nand/jz4740_nand.c b/target/linux/xburst/files-2.6.32/drivers/mtd/nand/jz4740_nand.c new file mode 100644 index 0000000..d1e3818 --- /dev/null +++ b/target/linux/xburst/files-2.6.32/drivers/mtd/nand/jz4740_nand.c @@ -0,0 +1,418 @@ +/* + * Copyright (C) 2009, Lars-Peter Clausen + * JZ4720/JZ4740 SoC NAND controller driver + * + * 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 +#include + +#include +#include +#include + +#include +#include + +#define JZ_REG_NAND_CTRL 0x50 +#define JZ_REG_NAND_ECC_CTRL 0x100 +#define JZ_REG_NAND_DATA 0x104 +#define JZ_REG_NAND_PAR0 0x108 +#define JZ_REG_NAND_PAR1 0x10C +#define JZ_REG_NAND_PAR2 0x110 +#define JZ_REG_NAND_IRQ_STAT 0x114 +#define JZ_REG_NAND_IRQ_CTRL 0x118 +#define JZ_REG_NAND_ERR(x) (0x11C + (x << 2)) + +#define JZ_NAND_ECC_CTRL_PAR_READY BIT(4) +#define JZ_NAND_ECC_CTRL_ENCODING BIT(3) +#define JZ_NAND_ECC_CTRL_RS BIT(2) +#define JZ_NAND_ECC_CTRL_RESET BIT(1) +#define JZ_NAND_ECC_CTRL_ENABLE BIT(0) + +#define JZ_NAND_STATUS_ERR_COUNT (BIT(31) | BIT(30) | BIT(29)) +#define JZ_NAND_STATUS_PAD_FINISH BIT(4) +#define JZ_NAND_STATUS_DEC_FINISH BIT(3) +#define JZ_NAND_STATUS_ENC_FINISH BIT(2) +#define JZ_NAND_STATUS_UNCOR_ERROR BIT(1) +#define JZ_NAND_STATUS_ERROR BIT(0) + +#define JZ_NAND_CTRL_ENABLE_CHIP(x) BIT(x << 1) +#define JZ_NAND_CTRL_ASSERT_CHIP(x) BIT((x << 1) + 1) + +#define JZ_NAND_DATA_ADDR ((void __iomem *)0xB8000000) +#define JZ_NAND_CMD_ADDR (JZ_NAND_DATA_ADDR + 0x8000) +#define JZ_NAND_ADDR_ADDR (JZ_NAND_DATA_ADDR + 0x10000) + +struct jz_nand { + struct mtd_info mtd; + struct nand_chip chip; + void __iomem *base; + struct resource *mem; + + struct jz_nand_platform_data *pdata; +}; + +static inline struct jz_nand *mtd_to_jz_nand(struct mtd_info *mtd) +{ + return container_of(mtd, struct jz_nand, mtd); +} + +static void jz_nand_cmd_ctrl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + struct nand_chip *chip = mtd->priv; + uint32_t reg; + + if (ctrl & NAND_CTRL_CHANGE) { + BUG_ON((ctrl & NAND_ALE) && (ctrl & NAND_CLE)); + if (ctrl & NAND_ALE) + chip->IO_ADDR_W = JZ_NAND_ADDR_ADDR; + else if (ctrl & NAND_CLE) + chip->IO_ADDR_W = JZ_NAND_CMD_ADDR; + else + chip->IO_ADDR_W = JZ_NAND_DATA_ADDR; + + reg = readl(nand->base + JZ_REG_NAND_CTRL); + if ( ctrl & NAND_NCE ) + reg |= JZ_NAND_CTRL_ASSERT_CHIP(0); + else + reg &= ~JZ_NAND_CTRL_ASSERT_CHIP(0); + writel(reg, nand->base + JZ_REG_NAND_CTRL); + } + if (dat != NAND_CMD_NONE) + writeb(dat, chip->IO_ADDR_W); +} + +static int jz_nand_dev_ready(struct mtd_info *mtd) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + return gpio_get_value_cansleep(nand->pdata->busy_gpio); +} + +static void jz_nand_hwctl(struct mtd_info *mtd, int mode) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + uint32_t reg; + + + writel(0, nand->base + JZ_REG_NAND_IRQ_STAT); + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + + reg |= JZ_NAND_ECC_CTRL_RESET; + reg |= JZ_NAND_ECC_CTRL_ENABLE; + reg |= JZ_NAND_ECC_CTRL_RS; + + switch(mode) { + case NAND_ECC_READ: + reg &= ~JZ_NAND_ECC_CTRL_ENCODING; + break; + case NAND_ECC_WRITE: + reg |= JZ_NAND_ECC_CTRL_ENCODING; + break; + default: + break; + } + + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); +} + +static int jz_nand_calculate_ecc_rs(struct mtd_info* mtd, const uint8_t* dat, + uint8_t *ecc_code) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + uint32_t reg, status; + int i; + + do { + status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); + } while(!(status & JZ_NAND_STATUS_ENC_FINISH)); + + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + reg &= ~JZ_NAND_ECC_CTRL_ENABLE; + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); + + for (i = 0; i < 9; ++i) { + ecc_code[i] = readb(nand->base + JZ_REG_NAND_PAR0 + i); + } + + return 0; +} + +static void correct_data(uint8_t *dat, int index, int mask) +{ + int offset = index & 0x7; + uint16_t data; + printk("correct: "); + + index += (index >> 3); + + data = dat[index]; + data |= dat[index+1] << 8; + + printk("0x%x -> ", data); + + mask ^= (data >> offset) & 0x1ff; + data &= ~(0x1ff << offset); + data |= (mask << offset); + + printk("0x%x\n", data); + + dat[index] = data & 0xff; + dat[index+1] = (data >> 8) & 0xff; +} + +static int jz_nand_correct_ecc_rs(struct mtd_info* mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + struct jz_nand *nand = mtd_to_jz_nand(mtd); + int i, error_count, index; + uint32_t reg, status, error; + + for(i = 0; i < 9; ++i) { + if (read_ecc[i] != 0xff) + break; + } + if (i == 9) { + for (i = 0; i < nand->chip.ecc.size; ++i) { + if (dat[i] != 0xff) + break; + } + if (i == nand->chip.ecc.size) + return 0; + } + + for(i = 0; i < 9; ++i) + writeb(read_ecc[i], nand->base + JZ_REG_NAND_PAR0 + i); + + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + reg |= JZ_NAND_ECC_CTRL_PAR_READY; + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); + + do { + status = readl(nand->base + JZ_REG_NAND_IRQ_STAT); + } while (!(status & JZ_NAND_STATUS_DEC_FINISH)); + + reg = readl(nand->base + JZ_REG_NAND_ECC_CTRL); + reg &= ~JZ_NAND_ECC_CTRL_ENABLE; + writel(reg, nand->base + JZ_REG_NAND_ECC_CTRL); + + if (status & JZ_NAND_STATUS_ERROR) { + if (status & JZ_NAND_STATUS_UNCOR_ERROR) { + printk("uncorrectable ecc:"); + for(i = 0; i < 9; ++i) + printk(" 0x%x", read_ecc[i]); + printk("\n"); + printk("uncorrectable data:"); + for(i = 0; i < 32; ++i) + printk(" 0x%x", dat[i]); + printk("\n"); + return -1; + } + + error_count = (status & JZ_NAND_STATUS_ERR_COUNT) >> 29; + + printk("error_count: %d %x\n", error_count, status); + + for(i = 0; i < error_count; ++i) { + error = readl(nand->base + JZ_REG_NAND_ERR(i)); + index = ((error >> 16) & 0x1ff) - 1; + if (index >= 0 && index < 512) { + correct_data(dat, index, error & 0x1ff); + } + } + + return error_count; + } + + return 0; +} + + + +#ifdef CONFIG_MTD_CMDLINE_PARTS +static const char *part_probes[] = {"cmdline", NULL}; +#endif + +static int __devinit jz_nand_probe(struct platform_device *pdev) +{ + int ret; + struct jz_nand *nand; + struct nand_chip *chip; + struct mtd_info *mtd; + struct jz_nand_platform_data *pdata = pdev->dev.platform_data; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *partition_info; + int num_partitions = 0; +#endif + + nand = kzalloc(sizeof(*nand), GFP_KERNEL); + if (!nand) { + dev_err(&pdev->dev, "Failed to allocate device structure.\n"); + return -ENOMEM; + } + + nand->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!nand->mem) { + dev_err(&pdev->dev, "Failed to get platform mmio memory\n"); + ret = -ENOENT; + goto err_free; + } + + nand->mem = request_mem_region(nand->mem->start, resource_size(nand->mem), + pdev->name); + + if (!nand->mem) { + dev_err(&pdev->dev, "Failed to request mmio memory region\n"); + ret = -EBUSY; + goto err_free; + } + + nand->base = ioremap(nand->mem->start, resource_size(nand->mem)); + + if (!nand->base) { + dev_err(&pdev->dev, "Faild to ioremap mmio memory region\n"); + ret = -EBUSY; + goto err_release_mem; + } + + if (pdata && gpio_is_valid(pdata->busy_gpio)) { + ret = gpio_request(pdata->busy_gpio, "jz nand busy line"); + if (ret) { + dev_err(&pdev->dev, "Failed to request busy gpio %d: %d\n", + pdata->busy_gpio, ret); + goto err_iounmap; + } + } + + mtd = &nand->mtd; + chip = &nand->chip; + mtd->priv = chip; + mtd->owner = THIS_MODULE; + mtd->name = "jz4740-nand"; + + chip->ecc.hwctl = jz_nand_hwctl; + + chip->ecc.calculate = jz_nand_calculate_ecc_rs; + chip->ecc.correct = jz_nand_correct_ecc_rs; + chip->ecc.mode = NAND_ECC_HW; + chip->ecc.size = 512; + chip->ecc.bytes = 9; + if (pdata) + chip->ecc.layout = pdata->ecc_layout; + + chip->chip_delay = 50; + chip->cmd_ctrl = jz_nand_cmd_ctrl; + + if (pdata && gpio_is_valid(pdata->busy_gpio)) + chip->dev_ready = jz_nand_dev_ready; + + chip->IO_ADDR_R = JZ_NAND_DATA_ADDR; + chip->IO_ADDR_W = JZ_NAND_DATA_ADDR; + + nand->pdata = pdata; + platform_set_drvdata(pdev, nand); + + ret = nand_scan_ident(mtd, 1); + if (ret) { + dev_err(&pdev->dev, "Failed to scan nand\n"); + goto err_gpio_free; + } + + if (pdata && pdata->ident_callback) { + pdata->ident_callback(pdev, chip, &pdata->partitions, &pdata->num_partitions); + } + + ret = nand_scan_tail(mtd); + if (ret) { + dev_err(&pdev->dev, "Failed to scan nand\n"); + goto err_gpio_free; + } + +#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + num_partitions = parse_mtd_partitions(mtd, part_probes, + &partition_info, 0); +#endif + if (num_partitions <= 0 && pdata) { + num_partitions = pdata->num_partitions; + partition_info = pdata->partitions; + } + + if (num_partitions > 0) + ret = add_mtd_partitions(mtd, partition_info, num_partitions); + else +#endif + ret = add_mtd_device(mtd); + + if (ret) { + dev_err(&pdev->dev, "Failed to add mtd device\n"); + goto err_nand_release; + } + + dev_info(&pdev->dev, "Successfully registered JZ4740 NAND driver\n"); + + return 0; +err_nand_release: + nand_release(&nand->mtd); +err_gpio_free: + platform_set_drvdata(pdev, NULL); + gpio_free(pdata->busy_gpio); +err_iounmap: + iounmap(nand->base); +err_release_mem: + release_mem_region(nand->mem->start, resource_size(nand->mem)); +err_free: + kfree(nand); + return ret; +} + +static void __devexit jz_nand_remove(struct platform_device *pdev) +{ + struct jz_nand *nand = platform_get_drvdata(pdev); + + nand_release(&nand->mtd); + + iounmap(nand->base); + + release_mem_region(nand->mem->start, resource_size(nand->mem)); + + platform_set_drvdata(pdev, NULL); + kfree(nand); +} + +struct platform_driver jz_nand_driver = { + .probe = jz_nand_probe, + .remove = __devexit_p(jz_nand_probe), + .driver = { + .name = "jz4740-nand", + .owner = THIS_MODULE, + }, +}; + +static int __init jz_nand_init(void) +{ + return platform_driver_register(&jz_nand_driver); +} +module_init(jz_nand_init); + +static void __exit jz_nand_exit(void) +{ + platform_driver_unregister(&jz_nand_driver); +} +module_exit(jz_nand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("NAND controller driver for JZ4720/JZ4740 SoC"); +MODULE_ALIAS("platform:jz4740-nand"); +MODULE_ALIAS("platform:jz4720-nand"); diff --git a/target/linux/xburst/files-2.6.32/drivers/power/jz4740-battery.c b/target/linux/xburst/files-2.6.32/drivers/power/jz4740-battery.c new file mode 100644 index 0000000..fe03d80 --- /dev/null +++ b/target/linux/xburst/files-2.6.32/drivers/power/jz4740-battery.c @@ -0,0 +1,471 @@ +/* + * Battery measurement code for Ingenic JZ SOC. + * + * based on tosa_battery.c + * + * Copyright (C) 2008 Marek Vasut + * Copyright (C) 2009 Jiejing Zhang + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +struct jz_battery_info { + struct power_supply usb; + struct power_supply bat; + struct power_supply ac; + int bat_status; + struct jz_batt_info *pdata; + struct mutex work_lock; + struct workqueue_struct *monitor_wqueue; + struct delayed_work bat_work; +}; + +#define ps_to_jz_battery(x) container_of((x), struct jz_battery_info, bat); + +/********************************************************************* + * Power + *********************************************************************/ + + +static int jz_get_power_prop(struct jz_battery_info *bat_info, + struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + int gpio; + + if (bat_info == 0 || bat_info->pdata == 0) + return -EINVAL; + gpio = (psy->type == POWER_SUPPLY_TYPE_MAINS) ? + bat_info->pdata->dc_dect_gpio : + bat_info->pdata->usb_dect_gpio; + if (!gpio_is_valid(gpio)) + return -EINVAL; + switch (psp) { + case POWER_SUPPLY_PROP_ONLINE: + val->intval = !gpio_get_value(gpio); + break; + default: + return -EINVAL; + } + + return 0; +} + +static int jz_usb_get_power_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct jz_battery_info *bat_info = container_of(psy, struct jz_battery_info, usb); + return jz_get_power_prop(bat_info, psy, psp, val); +} + +static int jz_ac_get_power_prop(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct jz_battery_info *bat_info = container_of(psy, struct jz_battery_info, ac); + return jz_get_power_prop(bat_info, psy, psp, val); +} + + +static enum power_supply_property jz_power_props[] = { + POWER_SUPPLY_PROP_ONLINE, +}; + +static struct power_supply jz_ac = { + .name = "ac", + .type = POWER_SUPPLY_TYPE_MAINS, + .properties = jz_power_props, + .num_properties = ARRAY_SIZE(jz_power_props), + .get_property = jz_ac_get_power_prop, +}; + +static struct power_supply jz_usb = { + .name = "usb", + .type = POWER_SUPPLY_TYPE_USB, + .properties = jz_power_props, + .num_properties = ARRAY_SIZE(jz_power_props), + .get_property = jz_usb_get_power_prop, +}; + + +/********************************************************************* + * Battery properties + *********************************************************************/ + +static long jz_read_bat(struct power_supply *psy) +{ + struct jz_battery_info *bat_info = ps_to_jz_battery(psy); + enum jz_adc_battery_scale scale; + + if (bat_info->pdata->max_voltag > 2500000) + scale = JZ_ADC_BATTERY_SCALE_7V5; + else + scale = JZ_ADC_BATTERY_SCALE_2V5; + + return jz4740_adc_read_battery_voltage(psy->dev->parent->parent, scale); +} + +static int jz_bat_get_capacity(struct power_supply *psy) +{ + int ret; + struct jz_battery_info *bat_info = ps_to_jz_battery(psy); + + ret = jz_read_bat(psy); + + if (ret < 0) + return ret; + + ret = (ret - bat_info->pdata->min_voltag) * 100 + / (bat_info->pdata->max_voltag - bat_info->pdata->min_voltag); + + if (ret > 100) + ret = 100; + else if (ret < 0) + ret = 0; + + return ret; +} + +static int jz_bat_get_property(struct power_supply *psy, + enum power_supply_property psp, + union power_supply_propval *val) +{ + struct jz_battery_info *bat_info = ps_to_jz_battery(psy) + + switch (psp) { + case POWER_SUPPLY_PROP_STATUS: + val->intval = bat_info->bat_status; + break; + case POWER_SUPPLY_PROP_TECHNOLOGY: + val->intval = bat_info->pdata->batt_tech; + break; + case POWER_SUPPLY_PROP_HEALTH: + if(jz_read_bat(psy) < bat_info->pdata->min_voltag) { + dev_dbg(psy->dev, "%s: battery is dead," + "voltage too low!\n", __func__); + val->intval = POWER_SUPPLY_HEALTH_DEAD; + } else { + dev_dbg(psy->dev, "%s: battery is good," + "voltage normal.\n", __func__); + val->intval = POWER_SUPPLY_HEALTH_GOOD; + } + break; + case POWER_SUPPLY_PROP_CAPACITY: + val->intval = jz_bat_get_capacity(psy); + dev_dbg(psy->dev, "%s: battery_capacity = %d\n", + __func__, val->intval); + break; + case POWER_SUPPLY_PROP_VOLTAGE_NOW: + val->intval = jz_read_bat(psy); + if (val->intval < 0) + return val->intval; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MAX: + case POWER_SUPPLY_PROP_VOLTAGE_MAX_DESIGN: + val->intval = bat_info->pdata->max_voltag; + break; + case POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN: + val->intval = bat_info->pdata->min_voltag; + break; + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + default: + return -EINVAL; + } + return 0; +} + +static void jz_bat_external_power_changed(struct power_supply *psy) +{ + struct jz_battery_info *bat_info = ps_to_jz_battery(psy); + + cancel_delayed_work(&bat_info->bat_work); + queue_delayed_work(bat_info->monitor_wqueue, &bat_info->bat_work, HZ / 8); +} + +static char *status_text[] = { + [POWER_SUPPLY_STATUS_UNKNOWN] = "Unknown", + [POWER_SUPPLY_STATUS_CHARGING] = "Charging", + [POWER_SUPPLY_STATUS_DISCHARGING] = "Discharging", + [POWER_SUPPLY_STATUS_NOT_CHARGING] = "Not charging", +}; + +static void jz_bat_update(struct power_supply *psy) +{ + struct jz_battery_info *bat_info = ps_to_jz_battery(psy); + + int old_status = bat_info->bat_status; + static unsigned long old_batt_vol = 0; + unsigned long batt_vol = jz_read_bat(psy); + + mutex_lock(&bat_info->work_lock); + + if (gpio_is_valid(bat_info->pdata->charg_stat_gpio)) { + if(!gpio_get_value(bat_info->pdata->charg_stat_gpio)) + bat_info->bat_status = POWER_SUPPLY_STATUS_CHARGING; + else + bat_info->bat_status = POWER_SUPPLY_STATUS_NOT_CHARGING; + dev_dbg(psy->dev, "%s: battery status=%s\n", + __func__, status_text[bat_info->bat_status]); + + if (old_status != bat_info->bat_status) { + dev_dbg(psy->dev, "%s %s -> %s\n", + psy->name, + status_text[old_status], + status_text[bat_info->bat_status]); + + power_supply_changed(psy); + } + } + + if (old_batt_vol - batt_vol > 50000) { + dev_dbg(psy->dev, "voltage change : %ld -> %ld\n", + old_batt_vol, batt_vol); + power_supply_changed(psy); + old_batt_vol = batt_vol; + } + + mutex_unlock(&bat_info->work_lock); +} + +static enum power_supply_property jz_bat_main_props[] = { + POWER_SUPPLY_PROP_STATUS, + POWER_SUPPLY_PROP_TECHNOLOGY, + POWER_SUPPLY_PROP_HEALTH, + POWER_SUPPLY_PROP_CAPACITY, /* in percents! */ + POWER_SUPPLY_PROP_VOLTAGE_NOW, + POWER_SUPPLY_PROP_VOLTAGE_MAX, + POWER_SUPPLY_PROP_VOLTAGE_MIN_DESIGN, + POWER_SUPPLY_PROP_PRESENT, +}; + +struct power_supply bat_ps = { + .name = "battery", + .type = POWER_SUPPLY_TYPE_BATTERY, + .properties = jz_bat_main_props, + .num_properties = ARRAY_SIZE(jz_bat_main_props), + .get_property = jz_bat_get_property, + .external_power_changed = jz_bat_external_power_changed, + .use_for_apm = 1, +}; + +static void jz_bat_work(struct work_struct *work) +{ + /* query interval too small will increase system workload*/ + const int interval = HZ * 30; + struct jz_battery_info *bat_info = container_of(work,struct jz_battery_info, bat_work.work); + + jz_bat_update(&bat_info->bat); + queue_delayed_work(bat_info->monitor_wqueue, + &bat_info->bat_work, interval); +} + +#ifdef CONFIG_PM +static int jz_bat_suspend(struct platform_device *pdev, pm_message_t state) +{ + struct jz_battery_info *bat_info = platform_get_drvdata(pdev); + + bat_info->bat_status = POWER_SUPPLY_STATUS_UNKNOWN; + + return 0; +} + +static int jz_bat_resume(struct platform_device *pdev) +{ + struct jz_battery_info *bat_info = platform_get_drvdata(pdev); + + bat_info->bat_status = POWER_SUPPLY_STATUS_UNKNOWN; + + cancel_delayed_work(&bat_info->bat_work); + queue_delayed_work(bat_info->monitor_wqueue, &bat_info->bat_work, HZ/10); + + return 0; +} +#else +#define jz_bat_suspend NULL +#define jz_bat_resume NULL +#endif + +static int jz_bat_probe(struct platform_device *pdev) +{ + int ret = 0; + struct jz_battery_info *bat_info; + + bat_info = kzalloc(sizeof(struct jz_battery_info), GFP_KERNEL); + + if (!bat_info) { + return -ENOMEM; + } + + if (!pdev->dev.platform_data) { + dev_err(&pdev->dev, "Please set battery info\n"); + ret = -EINVAL; + goto err_platform_data; + } + platform_set_drvdata(pdev, bat_info); + bat_info->pdata = pdev->dev.platform_data; + bat_info->bat = bat_ps; + bat_info->usb = jz_usb; + bat_info->ac = jz_ac; + mutex_init(&bat_info->work_lock); + INIT_DELAYED_WORK(&bat_info->bat_work, jz_bat_work); + + if (gpio_is_valid(bat_info->pdata->dc_dect_gpio)) { + ret = gpio_request(bat_info->pdata->dc_dect_gpio, "AC/DC DECT"); + if (ret) { + dev_err(&pdev->dev, "ac/dc dect gpio request failed.\n"); + + goto err_dc_gpio_request; + } + ret = gpio_direction_input(bat_info->pdata->dc_dect_gpio); + if (ret) { + dev_err(&pdev->dev, "ac/dc dect gpio direction failed.\n"); + + goto err_dc_gpio_direction; + } + } + + if (gpio_is_valid(bat_info->pdata->usb_dect_gpio)) { + ret = gpio_request(bat_info->pdata->usb_dect_gpio, "USB DECT"); + if (ret) { + dev_err(&pdev->dev, "usb dect gpio request failed.\n"); + + goto err_usb_gpio_request; + } + ret = gpio_direction_input(bat_info->pdata->usb_dect_gpio); + if (ret) { + dev_err(&pdev->dev, "usb dect gpio set direction failed.\n"); + goto err_usb_gpio_direction; + } + + jz_gpio_disable_pullup(bat_info->pdata->usb_dect_gpio); + /* TODO: Use generic gpio is better */ + } + + if (gpio_is_valid(bat_info->pdata->charg_stat_gpio)) { + ret = gpio_request(bat_info->pdata->charg_stat_gpio, "CHARG STAT"); + if (ret) { + dev_err(&pdev->dev, "charger state gpio request failed.\n"); + goto err_charg_gpio_request; + } + ret = gpio_direction_input(bat_info->pdata->charg_stat_gpio); + if (ret) { + dev_err(&pdev->dev, "charger state gpio set direction failed.\n"); + goto err_charg_gpio_direction; + } + } + + if (gpio_is_valid(bat_info->pdata->dc_dect_gpio)) { + ret = power_supply_register(&pdev->dev, &bat_info->ac); + if (ret) { + dev_err(&pdev->dev, "power supply ac/dc register failed.\n"); + goto err_power_register_ac; + } + } + + if (gpio_is_valid(bat_info->pdata->usb_dect_gpio)) { + ret = power_supply_register(&pdev->dev, &bat_info->usb); + if (ret) { + dev_err(&pdev->dev, "power supply usb register failed.\n"); + goto err_power_register_usb; + } + } + + if (gpio_is_valid(bat_info->pdata->charg_stat_gpio)) { + ret = power_supply_register(&pdev->dev, &bat_info->bat); + if (ret) { + dev_err(&pdev->dev, "power supply battery register failed.\n"); + goto err_power_register_bat; + } else { + bat_info->monitor_wqueue = create_singlethread_workqueue("jz_battery"); + if (!bat_info->monitor_wqueue) { + return -ESRCH; + } + queue_delayed_work(bat_info->monitor_wqueue, &bat_info->bat_work, HZ * 1); + } + } + printk(KERN_INFO "jz_bat init success.\n"); + return ret; + +err_power_register_bat: + power_supply_unregister(&bat_info->usb); +err_power_register_usb: + power_supply_unregister(&bat_info->ac); +err_power_register_ac: +err_charg_gpio_direction: + gpio_free(bat_info->pdata->charg_stat_gpio); +err_charg_gpio_request: +err_usb_gpio_direction: + gpio_free(bat_info->pdata->usb_dect_gpio); +err_usb_gpio_request: +err_dc_gpio_direction: + gpio_free(bat_info->pdata->dc_dect_gpio); +err_dc_gpio_request: +err_platform_data: + kfree(bat_info); + return ret; +} + +static int jz_bat_remove(struct platform_device *pdev) +{ + struct jz_battery_info *bat_info = platform_get_drvdata(pdev); + + if (bat_info->pdata) { + if (gpio_is_valid(bat_info->pdata->dc_dect_gpio)) + gpio_free(bat_info->pdata->dc_dect_gpio); + if (gpio_is_valid(bat_info->pdata->usb_dect_gpio)) + gpio_free(bat_info->pdata->usb_dect_gpio); + if (gpio_is_valid(bat_info->pdata->charg_stat_gpio)) + gpio_free(bat_info->pdata->charg_stat_gpio); + } + + power_supply_unregister(&bat_ps); + power_supply_unregister(&jz_ac); + power_supply_unregister(&jz_usb); + + return 0; +} + +static struct platform_driver jz_bat_driver = { + .probe = jz_bat_probe, + .remove = __devexit_p(jz_bat_remove), + .suspend = jz_bat_suspend, + .resume = jz_bat_resume, + .driver = { + .name = "jz4740-battery", + .owner = THIS_MODULE, + }, +}; + +static int __init jz_bat_init(void) +{ + return platform_driver_register(&jz_bat_driver); +} +module_init(jz_bat_init); + +static void __exit jz_bat_exit(void) +{ + platform_driver_unregister(&jz_bat_driver); +} +module_exit(jz_bat_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Jiejing Zhang "); +MODULE_DESCRIPTION("JZ4720/JZ4740 SoC battery driver"); diff --git a/target/linux/xburst/files-2.6.32/drivers/rtc/rtc-jz4740.c b/target/linux/xburst/files-2.6.32/drivers/rtc/rtc-jz4740.c new file mode 100644 index 0000000..190b54a --- /dev/null +++ b/target/linux/xburst/files-2.6.32/drivers/rtc/rtc-jz4740.c @@ -0,0 +1,325 @@ +/* + * Copyright (C) 2009, Lars-Peter Clausen + * JZ4720/JZ4740 SoC RTC driver + * + * 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 +#include +#include +#include +#include + +#define JZ_REG_RTC_CTRL 0x00 +#define JZ_REG_RTC_SEC 0x04 +#define JZ_REG_RTC_SEC_ALARM 0x08 +#define JZ_REG_REGULATOR 0x0C + +#define JZ_RTC_CTRL_WRDY BIT(7) +#define JZ_RTC_CTRL_1HZ BIT(6) +#define JZ_RTC_CTRL_1HZ_IRQ BIT(5) +#define JZ_RTC_CTRL_AF BIT(4) +#define JZ_RTC_CTRL_AF_IRQ BIT(3) +#define JZ_RTC_CTRL_AE BIT(2) +#define JZ_RTC_CTRL_ENABLE BIT(0) + +struct jz4740_rtc { + struct resource *mem; + void __iomem *base; + + struct rtc_device *rtc; + + unsigned int irq; + + spinlock_t lock; +}; + +static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg) +{ + return readl(rtc->base + reg); +} + +static inline void jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc) +{ + uint32_t ctrl; + do { + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); + } while (!(ctrl & JZ_RTC_CTRL_WRDY)); +} + + +static inline void jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg, + uint32_t val) +{ + jz4740_rtc_wait_write_ready(rtc); + writel(val, rtc->base + reg); +} + +static void jz4740_rtc_ctrl_set_bits(struct jz4740_rtc *rtc, uint32_t mask, + uint32_t val) +{ + unsigned long flags; + uint32_t ctrl; + + spin_lock_irqsave(&rtc->lock, flags); + + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); + + /* Don't clear interrupt flags by accident */ + ctrl |= JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF; + + ctrl &= ~mask; + ctrl |= val; + + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CTRL, ctrl); + + spin_unlock_irqrestore(&rtc->lock, flags); +} + +static inline struct jz4740_rtc *dev_to_rtc(struct device *dev) +{ + return dev_get_drvdata(dev); +} + +static int jz4740_rtc_read_time(struct device *dev, struct rtc_time *time) +{ + struct jz4740_rtc *rtc = dev_to_rtc(dev); + uint32_t secs, secs2; + + secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); + + while (secs != secs2) { + secs = secs2; + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC); + } + + rtc_time_to_tm(secs, time); + + return rtc_valid_tm(time); +} + +static int jz4740_rtc_set_mmss(struct device *dev, unsigned long secs) +{ + struct jz4740_rtc *rtc = dev_to_rtc(dev); + + if ((uint32_t)secs != secs) + return -EINVAL; + + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, secs); + + return 0; +} + +static int jz4740_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct jz4740_rtc *rtc = dev_to_rtc(dev); + uint32_t secs, secs2; + uint32_t ctrl; + + secs = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM); + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM); + + while (secs != secs2){ + secs = secs2; + secs2 = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_SEC_ALARM); + } + + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); + + alrm->enabled = !!(ctrl & JZ_RTC_CTRL_AE); + alrm->pending = !!(ctrl & JZ_RTC_CTRL_AF); + + rtc_time_to_tm(secs, &alrm->time); + + return rtc_valid_tm(&alrm->time); +} + +static int jz4740_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct jz4740_rtc *rtc = dev_to_rtc(dev); + unsigned long secs; + + rtc_tm_to_time(&alrm->time, &secs); + + if ((uint32_t)secs != secs) + return -EINVAL; + + jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC_ALARM, (uint32_t)secs); + jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AE, + alrm->enabled ? JZ_RTC_CTRL_AE : 0); + + return 0; +} + +static int jz4740_rtc_update_irq_enable(struct device *dev, unsigned int enabled) +{ + struct jz4740_rtc *rtc = dev_to_rtc(dev); + jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ_IRQ, + enabled ? JZ_RTC_CTRL_1HZ_IRQ : 0); + return 0; +} + + +static int jz4740_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct jz4740_rtc *rtc = dev_to_rtc(dev); + jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_AF_IRQ, + enabled ? JZ_RTC_CTRL_AF_IRQ : 0); + return 0; +} + +static struct rtc_class_ops jz4740_rtc_ops = { + .read_time = jz4740_rtc_read_time, + .set_mmss = jz4740_rtc_set_mmss, + .read_alarm = jz4740_rtc_read_alarm, + .set_alarm = jz4740_rtc_set_alarm, + .update_irq_enable = jz4740_rtc_update_irq_enable, + .alarm_irq_enable = jz4740_rtc_alarm_irq_enable, +}; + +static irqreturn_t jz4740_rtc_irq(int irq, void *data) +{ + struct jz4740_rtc *rtc = data; + uint32_t ctrl; + unsigned long events = 0; + ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL); + + if (ctrl & JZ_RTC_CTRL_1HZ) + events |= (RTC_UF | RTC_IRQF); + + if (ctrl & JZ_RTC_CTRL_AF) + events |= (RTC_AF | RTC_IRQF); + + rtc_update_irq(rtc->rtc, 1, events); + + jz4740_rtc_ctrl_set_bits(rtc, JZ_RTC_CTRL_1HZ | JZ_RTC_CTRL_AF, 0); + + return IRQ_HANDLED; +} + +static int __devinit jz4740_rtc_probe(struct platform_device *pdev) +{ + int ret; + struct jz4740_rtc *rtc; + + rtc = kmalloc(sizeof(*rtc), GFP_KERNEL); + + rtc->irq = platform_get_irq(pdev, 0); + + if (rtc->irq < 0) { + ret = -ENOENT; + dev_err(&pdev->dev, "Failed to get platform irq\n"); + goto err_free; + } + + rtc->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!rtc->mem) { + ret = -ENOENT; + dev_err(&pdev->dev, "Failed to get platform mmio memory\n"); + goto err_free; + } + + rtc->mem = request_mem_region(rtc->mem->start, resource_size(rtc->mem), + pdev->name); + + if (!rtc->mem) { + ret = -EBUSY; + dev_err(&pdev->dev, "Failed to request mmio memory region\n"); + goto err_free; + } + + rtc->base = ioremap_nocache(rtc->mem->start, resource_size(rtc->mem)); + + if (!rtc->base) { + ret = -EBUSY; + dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); + goto err_release_mem_region; + } + + platform_set_drvdata(pdev, rtc); + + rtc->rtc = rtc_device_register(pdev->name, &pdev->dev, &jz4740_rtc_ops, + THIS_MODULE); + + if (IS_ERR(rtc->rtc)) { + ret = PTR_ERR(rtc->rtc); + dev_err(&pdev->dev, "Failed to register rtc device: %d\n", ret); + goto err_iounmap; + } + + ret = request_irq(rtc->irq, jz4740_rtc_irq, 0, + pdev->name, rtc); + + if (ret) { + dev_err(&pdev->dev, "Failed to request rtc irq: %d\n", ret); + goto err_unregister_rtc; + } + printk("rtc-ctrl: %d\n", jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL)); + + return 0; + +err_unregister_rtc: + rtc_device_unregister(rtc->rtc); +err_iounmap: + platform_set_drvdata(pdev, NULL); + iounmap(rtc->base); +err_release_mem_region: + release_mem_region(rtc->mem->start, resource_size(rtc->mem)); +err_free: + kfree(rtc); + + return ret; +} + +static int __devexit jz4740_rtc_remove(struct platform_device *pdev) +{ + struct jz4740_rtc *rtc = platform_get_drvdata(pdev); + + rtc_device_unregister(rtc->rtc); + + iounmap(rtc->base); + release_mem_region(rtc->mem->start, resource_size(rtc->mem)); + + kfree(rtc); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +struct platform_driver jz4740_rtc_driver = { + .probe = jz4740_rtc_probe, + .remove = __devexit_p(jz4740_rtc_remove), + .driver = { + .name = "jz4740-rtc", + .owner = THIS_MODULE, + }, +}; + +static int __init jz4740_rtc_init(void) +{ + return platform_driver_register(&jz4740_rtc_driver); +} +module_init(jz4740_rtc_init); + +static void __exit jz4740_rtc_exit(void) +{ + platform_driver_unregister(&jz4740_rtc_driver); +} +module_exit(jz4740_rtc_exit); + +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("RTC driver for the JZ4720/JZ4740 SoC\n"); +MODULE_ALIAS("platform:jz4740-rtc"); +MODULE_ALIAS("platform:jz4720-rtc"); diff --git a/target/linux/xburst/files-2.6.32/drivers/usb/gadget/jz4740_udc.c b/target/linux/xburst/files-2.6.32/drivers/usb/gadget/jz4740_udc.c new file mode 100644 index 0000000..4a98d33 --- /dev/null +++ b/target/linux/xburst/files-2.6.32/drivers/usb/gadget/jz4740_udc.c @@ -0,0 +1,2410 @@ +/* + * linux/drivers/usb/gadget/jz4740_udc.c + * + * Ingenic JZ4740 on-chip high speed USB device controller + * + * Copyright (C) 2006 - 2008 Ingenic Semiconductor Inc. + * Author: + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "jz4740_udc.h" + +#define JZ_REG_UDC_FADDR 0x00 /* Function Address 8-bit */ +#define JZ_REG_UDC_POWER 0x01 /* Power Managemetn 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 + +/* Testmode register bits */ +#define USB_TEST_SE0NAK 0x01 +#define USB_TEST_J 0x02 +#define USB_TEST_K 0x04 +#define USB_TEST_PACKET 0x08 + +/* 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 + +static unsigned int udc_debug = 0; /* 0: normal mode, 1: test udc cable type mode */ + +module_param(udc_debug, int, 0); +MODULE_PARM_DESC(udc_debug, "test udc cable or power type"); + +static unsigned int use_dma = 0; /* 1: use DMA, 0: use PIO */ + +module_param(use_dma, int, 0); +MODULE_PARM_DESC(use_dma, "DMA mode enable flag"); + +struct jz4740_udc *the_controller; + +/* + * 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, uint8_t mask) +{ + usb_writew(udc, reg, usb_readw(udc, reg) | mask); +} + +static inline void usb_setl(struct jz4740_udc *udc, size_t reg, uint32_t mask) +{ + usb_writel(udc, reg, usb_readl(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 usb_clearl(struct jz4740_udc *udc, size_t reg, uint32_t mask) +{ + usb_writel(udc, reg, usb_readl(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, int max) +{ + uint8_t *buf; + int length, nlong, nbyte; + DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); + + buf = req->req.buf + req->req.actual; + prefetch(buf); + + length = req->req.length - req->req.actual; + length = min(length, max); + req->req.actual += length; + + DEBUG("Write %d (max %d), fifo %x\n", length, max, ep->fifo); + + nlong = length >> 2; + nbyte = length & 0x3; + while (nlong--) { + usb_writel(ep->dev, ep->fifo, *((uint32_t *)buf)); + buf += 4; + } + while (nbyte--) { + usb_writeb(ep->dev, ep->fifo, *buf++); + } + + return length; +} + +static inline int read_packet(struct jz4740_ep *ep, + struct jz4740_request *req, int count) +{ + uint8_t *buf; + int length, nlong, nbyte; + DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); + + buf = req->req.buf + req->req.actual; + prefetchw(buf); + + length = req->req.length - req->req.actual; + length = min(length, count); + req->req.actual += length; + + DEBUG("Read %d, fifo %x\n", length, ep->fifo); + + nlong = length >> 2; + nbyte = length & 0x3; + while (nlong--) { + *((uint32_t *)buf) = usb_readl(ep->dev, ep->fifo); + buf += 4; + } + while (nbyte--) { + *buf++ = usb_readb(ep->dev, ep->fifo); + } + + 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 */ +#ifdef CONFIG_SOC_JZ4740 + REG_CPM_SCR &= ~CPM_SCR_USBPHY_ENABLE; +#elif defined(CONFIG_SOC_JZ4750) || defined(CONFIG_SOC_JZ4750D) + REG_CPM_OPCR &= ~CPM_OPCR_UDCPHY_ENABLE; +#endif + + 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; + ep->pio_irqs = 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_set_index(dev, ep_index(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. + */ + __cpm_stop_udc(); + + /* Enable the USB PHY */ +#ifdef CONFIG_SOC_JZ4740 + REG_CPM_SCR |= CPM_SCR_USBPHY_ENABLE; +#elif defined(CONFIG_SOC_JZ4750) || defined(CONFIG_SOC_JZ4750D) + REG_CPM_OPCR |= CPM_OPCR_UDCPHY_ENABLE; +#endif + + /* 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. + */ + +int usb_gadget_register_driver(struct usb_gadget_driver *driver) +{ + struct jz4740_udc *dev = the_controller; + int retval; + + if (!driver || !driver->bind) { + return -EINVAL; + } + + if (!dev) { + return -ENODEV; + } + + if (dev->driver) { + return -EBUSY; + } + + /* hook up the driver */ + dev->driver = driver; + dev->gadget.dev.driver = &driver->driver; + + retval = driver->bind(&dev->gadget); + if (retval) { + DEBUG("%s: bind to driver %s --> error %d\n", dev->gadget.name, + driver->driver.name, retval); + dev->driver = 0; + return retval; + } + + /* then enable host detection and ep0; and we're ready + * for set_configuration as well as eventual disconnect. + */ + udc_enable(dev); + + DEBUG("%s: registered gadget driver '%s'\n", dev->gadget.name, + driver->driver.name); + + return 0; +} + +EXPORT_SYMBOL(usb_gadget_register_driver); + +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_set_index(dev, ep_index(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. + */ +int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) +{ + struct jz4740_udc *dev = the_controller; + unsigned long flags; + DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); + + if (!dev) + return -ENODEV; + if (!driver || driver != dev->driver) + return -EINVAL; + if (!driver->unbind) + return -EBUSY; + + spin_lock_irqsave(&dev->lock, flags); + dev->driver = 0; + stop_activity(dev, driver); + spin_unlock_irqrestore(&dev->lock, flags); + + driver->unbind(&dev->gadget); + + udc_disable(dev); + + DEBUG("unregistered driver '%s'\n", driver->driver.name); + + return 0; +} + +EXPORT_SYMBOL(usb_gadget_unregister_driver); + +/*-------------------------------------------------------------------------*/ + +/* + * Starting DMA using mode 1 + */ +static void kick_dma(struct jz4740_ep *ep, struct jz4740_request *req) +{ + struct jz4740_udc *dev = ep->dev; + uint32_t count = req->req.length; + uint32_t physaddr = virt_to_phys((void *)req->req.buf); + + DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); + + jz_udc_select_ep(ep); + + if (ep_is_in(ep)) { /* Bulk-IN transfer using DMA channel 1 */ + ep->reg_addr = JZ_REG_UDC_ADDR1; + + dma_cache_wback_inv((unsigned long)req->req.buf, count); + + pio_irq_enable(ep); + + usb_writeb(dev, JZ_REG_UDC_INCSRH, + USB_INCSRH_DMAREQENAB | USB_INCSRH_AUTOSET | USB_INCSRH_DMAREQMODE); + + usb_writel(dev, JZ_REG_UDC_ADDR1, physaddr); + usb_writel(dev, JZ_REG_UDC_COUNT1, count); + usb_writel(dev, JZ_REG_UDC_CNTL1, USB_CNTL_ENA | USB_CNTL_DIR_IN | USB_CNTL_MODE_1 | + USB_CNTL_INTR_EN | USB_CNTL_BURST_16 | USB_CNTL_EP(ep_index(ep))); + } + else { /* Bulk-OUT transfer using DMA channel 2 */ + ep->reg_addr = JZ_REG_UDC_ADDR2; + + dma_cache_wback_inv((unsigned long)req->req.buf, count); + + pio_irq_enable(ep); + + usb_setb(dev, JZ_REG_UDC_OUTCSRH, + USB_OUTCSRH_DMAREQENAB | USB_OUTCSRH_AUTOCLR | USB_OUTCSRH_DMAREQMODE); + + usb_writel(dev, JZ_REG_UDC_ADDR2, physaddr); + usb_writel(dev, JZ_REG_UDC_COUNT2, count); + usb_writel(dev, JZ_REG_UDC_CNTL2, USB_CNTL_ENA | USB_CNTL_MODE_1 | + USB_CNTL_INTR_EN | USB_CNTL_BURST_16 | USB_CNTL_EP(ep_index(ep))); + } +} + +/*-------------------------------------------------------------------------*/ + +/** 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; + uint32_t physaddr = virt_to_phys((void *)req->req.buf); + + DEBUG("%s:%s[%d]\n", __FILE__, __func__, __LINE__); + max = le16_to_cpu(ep->desc->wMaxPacketSize); + + if (use_dma) { + uint32_t dma_count; + + /* DMA interrupt generated due to the last packet loaded into the FIFO */ + + dma_count = usb_readl(dev, ep->reg_addr) - physaddr; + req->req.actual += dma_count; + + if (dma_count % max) { + /* If the last packet is less than MAXP, set INPKTRDY manually */ + usb_setb(dev, ep->csr, USB_INCSR_INPKTRDY); + } + + done(ep, req, 0); + if (list_empty(&ep->queue)) { + pio_irq_disable(ep); + return 1; + } + else { + /* advance the request queue */ + req = list_entry(ep->queue.next, struct jz4740_request, queue); + kick_dma(ep, req); + return 0; + } + } + + /* + * PIO mode handling starts here ... + */ + + 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; + uint32_t physaddr = virt_to_phys((void *)req->req.buf); + + if (use_dma) { + uint32_t dma_count; + + /* DMA interrupt generated due to a packet less than MAXP loaded into the FIFO */ + + dma_count = usb_readl(dev, ep->reg_addr) - physaddr; + req->req.actual += dma_count; + + /* Disable interrupt and DMA */ + pio_irq_disable(ep); + usb_writel(dev, JZ_REG_UDC_CNTL2, 0); + + /* Read all bytes from this packet */ + count = usb_readw(dev, JZ_REG_UDC_OUTCOUNT); + count = read_packet(ep, req, count); + + if (count) { + /* If the last packet is greater than zero, clear OUTPKTRDY manually */ + usb_clearb(dev, ep->csr, USB_OUTCSR_OUTPKTRDY); + } + done(ep, req, 0); + + if (!list_empty(&ep->queue)) { + /* advance the request queue */ + req = list_entry(ep->queue.next, struct jz4740_request, queue); + kick_dma(ep, req); + } + + return 1; + } + + /* + * PIO mode handling starts here ... + */ + + /* 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; + unsigned long flags; + 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) */ + spin_lock_irqsave(&ep->dev->lock, flags); + index = usb_readb(ep->dev, JZ_REG_UDC_INDEX); + + req->req.complete(&ep->ep, &req->req); + + /* Restore index */ + jz_udc_set_index(ep->dev, index); + spin_unlock_irqrestore(&ep->dev->lock, flags); + ep->stopped = stopped; +} + +/** Enable EP interrupt */ +static void pio_irq_enable(struct jz4740_ep *ep) +{ + uint8_t index = ep_index(ep); + struct jz4740_udc *dev = ep->dev; + DEBUG("%s: EP%d %s\n", __FUNCTION__, ep_index(ep), ep_is_in(ep) ? "IN": "OUT"); + + if (ep_is_in(ep)) { + switch (index) { + case 1: + case 2: + usb_setw(dev, JZ_REG_UDC_INTRINE, BIT(index)); + dev->in_mask |= BIT(index); + break; + default: + DEBUG("Unknown endpoint: %d\n", index); + break; + } + } + else { + switch (index) { + case 1: + usb_setw(dev, JZ_REG_UDC_INTROUTE, BIT(index)); + dev->out_mask |= BIT(index); + break; + default: + DEBUG("Unknown endpoint: %d\n", index); + break; + } + } +} + +/** Disable EP interrupt */ +static void pio_irq_disable(struct jz4740_ep *ep) +{ + uint8_t index = ep_index(ep); + struct jz4740_udc *dev = ep->dev; + + DEBUG("%s: EP%d %s\n", __FUNCTION__, ep_index(ep), ep_is_in(ep) ? "IN": "OUT"); + + if (ep_is_in(ep)) { + switch (ep_index(ep)) { + case 1: + case 2: + usb_clearw(ep->dev, JZ_REG_UDC_INTRINE, BIT(index)); + dev->in_mask &= ~BIT(index); + break; + default: + DEBUG("Unknown endpoint: %d\n", index); + break; + } + } + else { + switch (ep_index(ep)) { + case 1: + usb_clearw(ep->dev, JZ_REG_UDC_INTROUTE, BIT(index)); + dev->out_mask &= ~BIT(index); + break; + default: + DEBUG("Unknown endpoint: %d\n", index); + break; + } + } +} + +/* + * 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_set_index(dev, ep_index(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 = 0; + else + req = list_entry(ep->queue.next, struct jz4740_request, queue); + + DEBUG("req: %p\n", req); + + if (!req) + return; + + 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_set_index(dev, ep_index(ep)); + if (ep->desc) { + uint32_t csr; + + if (use_dma) { + /* DMA starts here ... */ + if (list_empty(&ep->queue)) + req = 0; + else + req = list_entry(ep->queue.next, struct jz4740_request, queue); + + if (req) + read_fifo(ep, req); + return; + } + + /* + * PIO mode starts here ... + */ + + 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); + } + } + + if (value) { + ep->stopped = 1; + } else { + ep->stopped = 0; + } + + 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_set_index(dev, desc->bEndpointAddress & 0x0F); + 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->pio_irqs = 0; + 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; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + req = kzalloc(sizeof(*req), gfp_flags); + if (!req) + return 0; + + 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; + + DEBUG("%s, %p\n", __FUNCTION__, ep); + + 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; + unsigned long flags; + + 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, 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 (use_dma) { + /* DMA */ + kick_dma(ep, req); + } + /* PIO */ + else if (ep_is_in(ep)) { + /* EP1 & EP2 */ + jz_udc_set_index(dev, ep_index(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_set_index(dev, ep_index(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, 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_set_index(ep->dev, ep_index(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_set_index(ep->dev, ep_index(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); + + dev->usb_address = 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_set_index(dev, ep_index(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__, dev->usb_address, + (dev->gadget.speed == USB_SPEED_HIGH) ? "HIGH":"FULL" ); +} + +/* + * jz4740 usb device interrupt handler. + */ +static irqreturn_t jz4740_udc_irq(int irq, void *_dev) +{ + struct jz4740_udc *dev = _dev; + uint8_t index; + + uint32_t intr_usb = usb_readb(dev, JZ_REG_UDC_INTRUSB) & 0x7; /* mask SOF */ + uint32_t intr_in = usb_readw(dev, JZ_REG_UDC_INTRIN); + uint32_t intr_out = usb_readw(dev, JZ_REG_UDC_INTROUT); + uint32_t intr_dma = usb_readb(dev, 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(&dev->lock); + index = usb_readb(dev, JZ_REG_UDC_INDEX); + + /* Check for resume from suspend mode */ + if ((intr_usb & USB_INTR_RESUME) && + (usb_readb(dev, JZ_REG_UDC_INTRUSBE) & USB_INTR_RESUME)) { + DEBUG("USB resume\n"); + dev->driver->resume(&dev->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(dev); + } + + /* Check for endpoint 0 interrupt */ + if (intr_in & USB_INTR_EP0) { + DEBUG("USB_INTR_EP0 (control)\n"); + jz4740_handle_ep0(dev, intr_in); + } + + /* Check for Bulk-IN DMA interrupt */ + if (intr_dma & 0x1) { + int ep_num; + struct jz4740_ep *ep; + ep_num = (usb_readl(dev, JZ_REG_UDC_CNTL1) >> 4) & 0xf; + ep = &dev->ep[ep_num + 1]; + jz_udc_set_index(dev, ep_num); + usb_setb(dev, ep->csr, USB_INCSR_INPKTRDY); +/* jz4740_in_epn(dev, ep_num, intr_in);*/ + } + + /* Check for Bulk-OUT DMA interrupt */ + if (intr_dma & 0x2) { + int ep_num; + ep_num = (usb_readl(dev, JZ_REG_UDC_CNTL2) >> 4) & 0xf; + jz4740_out_epn(dev, ep_num, intr_out); + } + + /* Check for each configured endpoint interrupt */ + if (intr_in & USB_INTR_INEP1) { + DEBUG("USB_INTR_INEP1\n"); + jz4740_in_epn(dev, 1, intr_in); + } + + if (intr_in & USB_INTR_INEP2) { + DEBUG("USB_INTR_INEP2\n"); + jz4740_in_epn(dev, 2, intr_in); + } + + if (intr_out & USB_INTR_OUTEP1) { + DEBUG("USB_INTR_OUTEP1\n"); + jz4740_out_epn(dev, 1, intr_out); + } + + /* Check for suspend mode */ + if ((intr_usb & USB_INTR_SUSPEND) && + (usb_readb(dev, JZ_REG_UDC_INTRUSBE) & USB_INTR_SUSPEND)) { + DEBUG("USB suspend\n"); + dev->driver->suspend(&dev->gadget); + /* Host unloaded from us, can do something, such as flushing + the NAND block cache etc. */ + } + + jz_udc_set_index(dev, index); + + spin_unlock(&dev->lock); + + return IRQ_HANDLED; +} + + + +/*-------------------------------------------------------------------------*/ + +/* Common functions - Added by River */ +static struct jz4740_udc udc_dev; + +static inline struct jz4740_udc *gadget_to_udc(struct usb_gadget *gadget) +{ + return container_of(gadget, struct jz4740_udc, gadget); +} +/* End added */ + +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, + /* current versions must always be self-powered */ +}; + +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 udc_dev = { + .usb_address = 0, + .gadget = { + .ops = &jz4740_udc_ops, + .ep0 = &udc_dev.ep[0].ep, + .name = "jz-udc", + .dev = { + .init_name = "gadget", + }, + }, + + /* control endpoint */ + .ep[0] = { + .ep = { + .name = "ep0", + .ops = &jz4740_ep_ops, + .maxpacket = EP0_MAXPACKETSIZE, + }, + .dev = &udc_dev, + + .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 = &udc_dev, + + .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 = &udc_dev, + + .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 = &udc_dev, + + .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 void gadget_release(struct device *_dev) +{ +} + + +static int jz4740_udc_probe(struct platform_device *pdev) +{ + struct jz4740_udc *dev = &udc_dev; + int ret; + + spin_lock_init(&dev->lock); + the_controller = dev; + + dev->dev = &pdev->dev; + dev_set_name(&dev->gadget.dev, "gadget"); + dev->gadget.dev.parent = &pdev->dev; + dev->gadget.dev.dma_mask = pdev->dev.dma_mask; + dev->gadget.dev.release = gadget_release; + + ret = device_register(&dev->gadget.dev); + if (ret) + return ret; + + platform_set_drvdata(pdev, dev); + + dev->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!dev->mem) { + ret = -ENOENT; + dev_err(&pdev->dev, "Failed to get mmio memory resource\n"); + goto err_device_unregister; + } + + dev->mem = request_mem_region(dev->mem->start, resource_size(dev->mem), pdev->name); + + if (!dev->mem) { + ret = -EBUSY; + dev_err(&pdev->dev, "Failed to request mmio memory region\n"); + goto err_device_unregister; + } + + dev->base = ioremap(dev->mem->start, resource_size(dev->mem)); + + if (!dev->base) { + ret = -EBUSY; + dev_err(&pdev->dev, "Failed to ioremap mmio memory\n"); + goto err_release_mem_region; + } + + dev->irq = platform_get_irq(pdev, 0); + + ret = request_irq(dev->irq, jz4740_udc_irq, IRQF_DISABLED, + pdev->name, dev); + if (ret) { + dev_err(&pdev->dev, "Failed to request irq: %d\n", ret); + goto err_iounmap; + } + + udc_disable(dev); + udc_reinit(dev); + + return 0; + +err_iounmap: + iounmap(dev->base); +err_release_mem_region: + release_mem_region(dev->mem->start, resource_size(dev->mem)); +err_device_unregister: + device_unregister(&dev->gadget.dev); + platform_set_drvdata(pdev, NULL); + + the_controller = 0; + + return ret; +} + +static int jz4740_udc_remove(struct platform_device *pdev) +{ + struct jz4740_udc *dev = platform_get_drvdata(pdev); + + if (dev->driver) + return -EBUSY; + + udc_disable(dev); +#ifdef UDC_PROC_FILE + remove_proc_entry(proc_node_name, NULL); +#endif + + free_irq(dev->irq, dev); + iounmap(dev->base); + release_mem_region(dev->mem->start, resource_size(dev->mem)); + + platform_set_drvdata(pdev, NULL); + device_unregister(&dev->gadget.dev); + the_controller = NULL; + + return 0; +} + +static struct platform_driver udc_driver = { + .probe = jz4740_udc_probe, + .remove = jz4740_udc_remove, + .driver = { + .name = "jz-udc", + .owner = THIS_MODULE, + }, +}; + +/*-------------------------------------------------------------------------*/ + +static int __init udc_init (void) +{ + return platform_driver_register(&udc_driver); +} + +static void __exit udc_exit (void) +{ + platform_driver_unregister(&udc_driver); +} + +module_init(udc_init); +module_exit(udc_exit); + +MODULE_DESCRIPTION("JZ4740 USB Device Controller"); +MODULE_AUTHOR("Wei Jianli "); +MODULE_LICENSE("GPL"); diff --git a/target/linux/xburst/files-2.6.32/drivers/usb/gadget/jz4740_udc.h b/target/linux/xburst/files-2.6.32/drivers/usb/gadget/jz4740_udc.h new file mode 100644 index 0000000..d95a082 --- /dev/null +++ b/target/linux/xburst/files-2.6.32/drivers/usb/gadget/jz4740_udc.h @@ -0,0 +1,97 @@ +/* + * linux/drivers/usb/gadget/jz4740_udc.h + * + * Ingenic JZ4740 on-chip high speed USB device controller + * + * Copyright (C) 2006 Ingenic Semiconductor Inc. + * Author: + * + * 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 + +/*-------------------------------------------------------------------------*/ + +typedef enum ep_type { + ep_control, ep_bulk_in, ep_bulk_out, ep_interrupt +} ep_type_t; + +struct jz4740_ep { + struct usb_ep ep; + struct jz4740_udc *dev; + + const struct usb_endpoint_descriptor *desc; + unsigned long pio_irqs; + + uint8_t stopped; + uint8_t bEndpointAddress; + uint8_t bmAttributes; + + ep_type_t type; + size_t fifo; + u32 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; + + enum ep0state ep0state; + struct jz4740_ep ep[UDC_MAX_ENDPOINTS]; + + unsigned char usb_address; + + udc_state_t state; + + struct resource *mem; + void __iomem *base; + int irq; + uint32_t in_mask; + uint32_t out_mask; +}; + +extern struct jz4740_udc *the_controller; + +#define ep_is_in(EP) (((EP)->bEndpointAddress&USB_DIR_IN)==USB_DIR_IN) +#define ep_maxpacket(EP) ((EP)->ep.maxpacket) +#define ep_index(EP) ((EP)->bEndpointAddress&0xF) + +#endif /* __USB_GADGET_JZ4740_H__ */ diff --git a/target/linux/xburst/files-2.6.32/drivers/video/backlight/gpm940b0.c b/target/linux/xburst/files-2.6.32/drivers/video/backlight/gpm940b0.c new file mode 100644 index 0000000..5cfb12f --- /dev/null +++ b/target/linux/xburst/files-2.6.32/drivers/video/backlight/gpm940b0.c @@ -0,0 +1,255 @@ +/* + * Copyright (C) 2009, Lars-Peter Clausen + * JZ4720/JZ4740 SoC LCD framebuffer driver + * + * 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 +#include +#include +#include +#include + +struct gpm940b0 { + struct spi_device *spi; + struct lcd_device *lcd; + struct backlight_device *bl; + unsigned enabled:1; +}; + +static int gpm940b0_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 void gpm940b0_power_disable(struct gpm940b0 *gpm940b0) +{ + int ret = gpm940b0_write_reg(gpm940b0->spi, 0x5, 0xc6) ; + if (ret < 0) + printk("Failed to disable power: %d\n", ret); +} + +static void gpm940b0_power_enable(struct gpm940b0 *gpm940b0) +{ + gpm940b0_write_reg(gpm940b0->spi, 0x5, 0xc7); +} + + +static int gpm940b0_set_power(struct lcd_device *lcd, int power) +{ + struct gpm940b0 *gpm940b0 = lcd_get_data(lcd); + + switch (power) { + case FB_BLANK_UNBLANK: + mdelay(20); + gpm940b0->enabled = 1; + gpm940b0_power_enable(gpm940b0); + break; + default: + gpm940b0->enabled = 0; + gpm940b0_power_disable(gpm940b0); + mdelay(20); + break; + } + return 0; +} + +static int gpm940b0_set_contrast(struct lcd_device *lcd, int contrast) +{ + struct gpm940b0 *gpm940b0 = lcd_get_data(lcd); + gpm940b0_write_reg(gpm940b0->spi, 0x0d, contrast); + return 0; +} + +static int gpm940b0_set_mode(struct lcd_device *lcd, struct fb_videomode *mode) +{ + if (mode->xres != 320 && mode->yres != 240) + return -EINVAL; + + return 0; +} + +/* +int gpm940b0_bl_update_status(struct backlight_device *bl) +{ + struct gpm940b0 *gpm940b0 = bl_get_data(bl); + + gpm940b0->reg5 &= ~0x38; + gpm940b0->reg5 |= ((bl->props.brightness << 3) & 0x38); + + gpm940b0_write_reg(gpm940b0->spi, 0x5, gpm940b0->reg5); + + return 0; +}*/ + +static ssize_t reg_write(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + char *buf2; + uint32_t reg = simple_strtoul(buf, &buf2, 10); + uint32_t val = simple_strtoul(buf2 + 1, NULL, 10); + struct gpm940b0 *gpm940b0 = dev_get_drvdata(dev); + + if (reg < 0 || val < 0) + return -EINVAL; + + gpm940b0_write_reg(gpm940b0->spi, reg, val); + return count; +} + +static DEVICE_ATTR(reg, 0644, NULL, reg_write); + +static struct lcd_ops gpm940b0_lcd_ops = { + .set_power = gpm940b0_set_power, + .set_contrast = gpm940b0_set_contrast, + .set_mode = gpm940b0_set_mode, +}; + +#if 0 +static struct backlight_ops gpm940b0_bl_ops = { +/* .get_brightness = gpm940b0_bl_get_brightness,*/ + .update_status = gpm940b0_bl_update_status, +}; +#endif + +static int __devinit gpm940b0_probe(struct spi_device *spi) +{ + int ret; + struct gpm940b0 *gpm940b0; + + gpm940b0 = kmalloc(sizeof(*gpm940b0), GFP_KERNEL); + + spi->bits_per_word = 8; + + ret = spi_setup(spi); + if (ret) { + dev_err(&spi->dev, "Failed to setup spi\n"); + goto err_free_gpm940b0; + } + + gpm940b0->spi = spi; + + gpm940b0->lcd = lcd_device_register("gpm940b0-lcd", &spi->dev, gpm940b0, + &gpm940b0_lcd_ops); + + if (IS_ERR(gpm940b0->lcd)) { + ret = PTR_ERR(gpm940b0->lcd); + dev_err(&spi->dev, "Failed to register lcd device: %d\n", ret); + goto err_free_gpm940b0; + } + + gpm940b0->lcd->props.max_contrast = 255; + +#if 0 + gpm940b0->bl = backlight_device_register("gpm940b0-bl", &spi->dev, gpm940b0, + &gpm940b0_bl_ops); + + if (IS_ERR(gpm940b0->bl)) { + ret = PTR_ERR(gpm940b0->bl); + dev_err(&spi->dev, "Failed to register backlight device: %d\n", ret); + gpm940b0->bl = NULL; + } else { + gpm940b0->bl->props.max_brightness = 8; + gpm940b0->bl->props.brightness = 0; + gpm940b0->bl->props.power = FB_BLANK_UNBLANK; + } +#endif + + ret = device_create_file(&spi->dev, &dev_attr_reg); + if (ret) + goto err_unregister_lcd; + + gpm940b0->enabled = 1; + dev_set_drvdata(&spi->dev, gpm940b0); + + gpm940b0_write_reg(spi, 0x13, 0x01); + gpm940b0_write_reg(spi, 0x5, 0xc7); + return 0; +err_unregister_lcd: + lcd_device_unregister(gpm940b0->lcd); +err_free_gpm940b0: + kfree(gpm940b0); + return ret; +} + +static int __devexit gpm940b0_remove(struct spi_device *spi) +{ + struct gpm940b0 *gpm940b0 = spi_get_drvdata(spi); +#if 0 + if (gpm940b0->bl) + backlight_device_unregister(gpm940b0->bl); +#endif + + lcd_device_unregister(gpm940b0->lcd); + + spi_set_drvdata(spi, NULL); + kfree(gpm940b0); + return 0; +} + +#ifdef CONFIG_PM + +static int gpm940b0_suspend(struct spi_device *spi, pm_message_t state) +{ + struct gpm940b0 *gpm940b0 = spi_get_drvdata(spi); + if (gpm940b0->enabled) { + gpm940b0_power_disable(gpm940b0); + mdelay(10); + } + return 0; +} + +static int gpm940b0_resume(struct spi_device *spi) +{ + struct gpm940b0 *gpm940b0 = spi_get_drvdata(spi); + if (gpm940b0->enabled) + gpm940b0_power_enable(gpm940b0); + return 0; +} + +#else +#define gpm940b0_suspend NULL +#define gpm940b0_resume NULL +#endif + +static struct spi_driver gpm940b0_driver = { + .driver = { + .name = "gpm940b0", + .owner = THIS_MODULE, + }, + .probe = gpm940b0_probe, + .remove = __devexit_p(gpm940b0_remove), + .suspend = gpm940b0_suspend, + .resume = gpm940b0_resume, +}; + +static int __init gpm940b0_init(void) +{ + return spi_register_driver(&gpm940b0_driver); +} +module_init(gpm940b0_init); + +static void __exit gpm940b0_exit(void) +{ + return spi_unregister_driver(&gpm940b0_driver); +} +module_exit(gpm940b0_exit) + +MODULE_AUTHOR("Lars-Peter Clausen"); +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("LCD and backlight controll for Giantplus GPM940B0"); +MODULE_ALIAS("spi:gpm940b0"); diff --git a/target/linux/xburst/files-2.6.32/drivers/video/jz4740_fb.c b/target/linux/xburst/files-2.6.32/drivers/video/jz4740_fb.c new file mode 100644 index 0000000..7aac458 --- /dev/null +++ b/target/linux/xburst/files-2.6.32/drivers/video/jz4740_fb.c @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2009, Lars-Peter Clausen + * JZ4720/JZ4740 SoC LCD framebuffer driver + * + * 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 +#include +#include +#include +#include +#include + +#include +#include + +#include + +#define JZ_REG_LCD_CFG 0x00 +#define JZ_REG_LCD_VSYNC 0x04 +#define JZ_REG_LCD_HSYNC 0x08 +#define JZ_REG_LCD_VAT 0x0C +#define JZ_REG_LCD_DAH 0x10 +#define JZ_REG_LCD_DAV 0x14 +#define JZ_REG_LCD_PS 0x18 +#define JZ_REG_LCD_CLS 0x1C +#define JZ_REG_LCD_SPL 0x20 +#define JZ_REG_LCD_REV 0x24 +#define JZ_REG_LCD_CTRL 0x30 +#define JZ_REG_LCD_STATE 0x34 +#define JZ_REG_LCD_IID 0x38 +#define JZ_REG_LCD_DA0 0x40 +#define JZ_REG_LCD_SA0 0x44 +#define JZ_REG_LCD_FID0 0x48 +#define JZ_REG_LCD_CMD0 0x4C +#define JZ_REG_LCD_DA1 0x50 +#define JZ_REG_LCD_SA1 0x54 +#define JZ_REG_LCD_FID1 0x58 +#define JZ_REG_LCD_CMD1 0x5C + +#define JZ_LCD_CFG_SLCD BIT(31) +#define JZ_LCD_CFG_PSM BIT(23) +#define JZ_LCD_CFG_CLSM BIT(22) +#define JZ_LCD_CFG_SPLM BIT(21) +#define JZ_LCD_CFG_REVM BIT(20) +#define JZ_LCD_CFG_HSYNCM BIT(19) +#define JZ_LCD_CFG_PCLKM BIT(18) +#define JZ_LCD_CFG_INV BIT(17) +#define JZ_LCD_CFG_SYNC_DIR BIT(16) +#define JZ_LCD_CFG_PSP BIT(15) +#define JZ_LCD_CFG_CLSP BIT(14) +#define JZ_LCD_CFG_SPLP BIT(13) +#define JZ_LCD_CFG_REVP BIT(12) +#define JZ_LCD_CFG_HSYNCP BIT(11) +#define JZ_LCD_CFG_PCLKP BIT(10) +#define JZ_LCD_CFG_DEP BIT(9) +#define JZ_LCD_CFG_VSYNCP BIT(8) +#define JZ_LCD_CFG_18_BIT BIT(7) +#define JZ_LCD_CFG_PDW BIT(5) | BIT(4) +#define JZ_LCD_CFG_MODE_MASK 0xf + +#define JZ_LCD_CTRL_BURST_4 (0x0 << 28) +#define JZ_LCD_CTRL_BURST_8 (0x1 << 28) +#define JZ_LCD_CTRL_BURST_16 (0x2 << 28) +#define JZ_LCD_CTRL_RGB555 BIT(27) +#define JZ_LCD_CTRL_OFUP BIT(26) +#define JZ_LCD_CTRL_FRC_GRAYSCALE_16 (0x0 << 24) +#define JZ_LCD_CTRL_FRC_GRAYSCALE_4 (0x1 << 24) +#define JZ_LCD_CTRL_FRC_GRAYSCALE_2 (0x2 << 24) +#define JZ_LCD_CTRL_PDD_MASK (0xff << 16) +#define JZ_LCD_CTRL_EOF_IRQ BIT(13) +#define JZ_LCD_CTRL_SOF_IRQ BIT(12) +#define JZ_LCD_CTRL_OFU_IRQ BIT(11) +#define JZ_LCD_CTRL_IFU0_IRQ BIT(10) +#define JZ_LCD_CTRL_IFU1_IRQ BIT(9) +#define JZ_LCD_CTRL_DD_IRQ BIT(8) +#define JZ_LCD_CTRL_QDD_IRQ BIT(7) +#define JZ_LCD_CTRL_REVERSE_ENDIAN BIT(6) +#define JZ_LCD_CTRL_LSB_FISRT BIT(5) +#define JZ_LCD_CTRL_DISABLE BIT(4) +#define JZ_LCD_CTRL_ENABLE BIT(3) +#define JZ_LCD_CTRL_BPP_1 0x0 +#define JZ_LCD_CTRL_BPP_2 0x1 +#define JZ_LCD_CTRL_BPP_4 0x2 +#define JZ_LCD_CTRL_BPP_8 0x3 +#define JZ_LCD_CTRL_BPP_15_16 0x4 +#define JZ_LCD_CTRL_BPP_18_24 0x5 + +#define JZ_LCD_CMD_SOF_IRQ BIT(15) +#define JZ_LCD_CMD_EOF_IRQ BIT(16) +#define JZ_LCD_CMD_ENABLE_PAL BIT(12) + +#define JZ_LCD_SYNC_MASK 0x3ff + +#define JZ_LCD_STATE_DISABLED BIT(0) + +struct jzfb_framedesc { + uint32_t next; + uint32_t addr; + uint32_t id; + uint32_t cmd; +} __attribute__((packed)); + +struct jzfb { + struct fb_info *fb; + struct platform_device *pdev; + void __iomem *base; + struct resource *mem; + struct jz4740_fb_platform_data *pdata; + + void *devmem; + size_t devmem_size; + dma_addr_t devmem_phys; + void *vidmem; + size_t vidmem_size; + dma_addr_t vidmem_phys; + struct jzfb_framedesc *framedesc; + + struct clk *ldclk; + struct clk *lpclk; + + uint32_t pseudo_palette[16]; + unsigned is_enabled:1; +}; + +static struct fb_fix_screeninfo jzfb_fix __devinitdata = { + .id = "JZ4740 FB", + .type = FB_TYPE_PACKED_PIXELS, + .visual = FB_VISUAL_TRUECOLOR, + .xpanstep = 0, + .ypanstep = 0, + .ywrapstep = 0, + .accel = FB_ACCEL_NONE, +}; + +const static struct jz_gpio_bulk_request jz_lcd_pins[] = { + JZ_GPIO_BULK_PIN(LCD_PCLK), + JZ_GPIO_BULK_PIN(LCD_HSYNC), + JZ_GPIO_BULK_PIN(LCD_VSYNC), + JZ_GPIO_BULK_PIN(LCD_DATA0), + JZ_GPIO_BULK_PIN(LCD_DATA1), + JZ_GPIO_BULK_PIN(LCD_DATA2), + JZ_GPIO_BULK_PIN(LCD_DATA3), + JZ_GPIO_BULK_PIN(LCD_DATA4), + JZ_GPIO_BULK_PIN(LCD_DATA5), + JZ_GPIO_BULK_PIN(LCD_DATA6), + JZ_GPIO_BULK_PIN(LCD_DATA7), +}; + + +int jzfb_setcolreg(unsigned regno, unsigned red, unsigned green, unsigned blue, + unsigned transp, struct fb_info *fb) +{ + ((uint32_t*)fb->pseudo_palette)[regno] = red << 16 | green << 8 | blue; + return 0; +} + +static int jzfb_get_controller_bpp(struct jzfb *jzfb) +{ + switch(jzfb->pdata->bpp) { + case 18: + case 24: + return 32; + break; + default: + return jzfb->pdata->bpp; + } +} + +static int jzfb_check_var(struct fb_var_screeninfo *var, struct fb_info *fb) +{ + struct jzfb* jzfb = fb->par; + struct fb_videomode *mode = jzfb->pdata->modes; + int i; + + if (fb->var.bits_per_pixel != jzfb_get_controller_bpp(jzfb) && + fb->var.bits_per_pixel != jzfb->pdata->bpp) + return -EINVAL; + + for (i = 0; i < jzfb->pdata->num_modes; ++i, ++mode) { + if (mode->xres == fb->var.xres && mode->yres == fb->var.yres) + break; + } + + if (i == jzfb->pdata->num_modes) + return -EINVAL; + + fb_videomode_to_var(&fb->var, fb->mode); + + switch (jzfb->pdata->bpp) { + case 8: + break; + case 15: + var->red.offset = 10; + var->red.length = 5; + var->green.offset = 6; + var->green.length = 5; + var->blue.offset = 0; + var->blue.length = 5; + break; + case 16: + var->red.offset = 11; + var->red.length = 5; + var->green.offset = 6; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 5; + break; + case 18: + var->red.offset = 16; + var->red.length = 6; + var->green.offset = 8; + var->green.length = 6; + var->blue.offset = 0; + var->blue.length = 6; + fb->var.bits_per_pixel = 32; + break; + case 32: + case 24: + var->transp.offset = 24; + var->transp.length = 8; + var->red.offset = 16; + var->red.length = 8; + var->green.offset = 8; + var->green.length = 8; + var->blue.offset = 0; + var->blue.length = 8; + fb->var.bits_per_pixel = 32; + break; + default: + break; + } + + return 0; +} + +static int jzfb_set_par(struct fb_info *info) +{ + struct jzfb* jzfb = info->par; + struct fb_var_screeninfo *var = &info->var; + uint16_t hds, vds; + uint16_t hde, vde; + uint16_t ht, vt; + uint32_t ctrl; + + hds = var->hsync_len + var->left_margin; + hde = hds + var->xres; + ht = hde + var->right_margin; + + vds = var->vsync_len + var->upper_margin; + vde = vds + var->yres; + vt = vde + var->lower_margin; + + writel(var->hsync_len, jzfb->base + JZ_REG_LCD_HSYNC); + writel(var->vsync_len, jzfb->base + JZ_REG_LCD_VSYNC); + + writel((ht << 16) | vt, jzfb->base + JZ_REG_LCD_VAT); + + writel((hds << 16) | hde, jzfb->base + JZ_REG_LCD_DAH); + writel((vds << 16) | vde, jzfb->base + JZ_REG_LCD_DAV); + + ctrl = JZ_LCD_CTRL_OFUP | JZ_LCD_CTRL_BURST_16; + ctrl |= JZ_LCD_CTRL_ENABLE; + + switch (jzfb->pdata->bpp) { + case 1: + ctrl |= JZ_LCD_CTRL_BPP_1; + break; + case 2: + ctrl |= JZ_LCD_CTRL_BPP_2; + break; + case 4: + ctrl |= JZ_LCD_CTRL_BPP_4; + break; + case 8: + ctrl |= JZ_LCD_CTRL_BPP_8; + break; + case 15: + ctrl |= JZ_LCD_CTRL_RGB555; /* Falltrough */ + case 16: + ctrl |= JZ_LCD_CTRL_BPP_15_16; + break; + case 18: + case 24: + case 32: + ctrl |= JZ_LCD_CTRL_BPP_18_24; + break; + default: + break; + } + writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); + + return 0; +} + +static int jzfb_blank(int blank_mode, struct fb_info *info) +{ + struct jzfb* jzfb = info->par; + uint32_t ctrl = readl(jzfb->base + JZ_REG_LCD_CTRL); + + switch (blank_mode) { + case FB_BLANK_UNBLANK: + if (jzfb->is_enabled) + return 0; + + jz_gpio_bulk_resume(jz_lcd_pins, ARRAY_SIZE(jz_lcd_pins)); + clk_enable(jzfb->ldclk); + clk_enable(jzfb->lpclk); + + writel(0, jzfb->base + JZ_REG_LCD_STATE); + + writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0); + + ctrl |= JZ_LCD_CTRL_ENABLE; + ctrl &= ~JZ_LCD_CTRL_DISABLE; + writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); + + jzfb->is_enabled = 1; + break; + default: + if (!jzfb->is_enabled) + return 0; + + ctrl |= JZ_LCD_CTRL_DISABLE; + writel(ctrl, jzfb->base + JZ_REG_LCD_CTRL); + do { + ctrl = readl(jzfb->base + JZ_REG_LCD_STATE); + } while (!(ctrl & JZ_LCD_STATE_DISABLED)); + + clk_disable(jzfb->lpclk); + clk_disable(jzfb->ldclk); + jz_gpio_bulk_suspend(jz_lcd_pins, ARRAY_SIZE(jz_lcd_pins)); + jzfb->is_enabled = 0; + break; + } + + return 0; +} + + +static int jzfb_alloc_vidmem(struct jzfb *jzfb) +{ + size_t devmem_size; + int max_videosize = 0; + struct fb_videomode *mode = jzfb->pdata->modes; + struct jzfb_framedesc *framedesc; + void *page; + int i; + + for (i = 0; i < jzfb->pdata->num_modes; ++mode, ++i) { + if (max_videosize < mode->xres * mode->yres) + max_videosize = mode->xres * mode->yres; + } + + max_videosize *= jzfb_get_controller_bpp(jzfb) >> 3; + + devmem_size = max_videosize + sizeof(struct jzfb_framedesc); + + jzfb->devmem_size = devmem_size; + jzfb->devmem = dma_alloc_coherent(&jzfb->pdev->dev, + PAGE_ALIGN(devmem_size), + &jzfb->devmem_phys, GFP_KERNEL); + + if (!jzfb->devmem) { + return -ENOMEM; + } + + for (page = jzfb->vidmem; + page < jzfb->vidmem + PAGE_ALIGN(jzfb->vidmem_size); + page += PAGE_SIZE) { + SetPageReserved(virt_to_page(page)); + } + + + framedesc = jzfb->devmem + max_videosize; + jzfb->vidmem = jzfb->devmem; + jzfb->vidmem_phys = jzfb->devmem_phys; + + framedesc->next = jzfb->devmem_phys + max_videosize; + framedesc->addr = jzfb->devmem_phys; + framedesc->id = 0; + framedesc->cmd = 0; + framedesc->cmd |= max_videosize / 4; + + jzfb->framedesc = framedesc; + + + return 0; +} + +static void jzfb_free_devmem(struct jzfb *jzfb) +{ + dma_free_coherent(&jzfb->pdev->dev, jzfb->devmem_size, jzfb->devmem, + jzfb->devmem_phys); +} + +static struct fb_ops jzfb_ops = { + .owner = THIS_MODULE, + .fb_check_var = jzfb_check_var, + .fb_set_par = jzfb_set_par, + .fb_blank = jzfb_blank, + .fb_fillrect = sys_fillrect, + .fb_copyarea = sys_copyarea, + .fb_imageblit = sys_imageblit, + .fb_setcolreg = jzfb_setcolreg, +}; + +static int __devinit jzfb_probe(struct platform_device *pdev) +{ + int ret; + struct jzfb *jzfb; + struct fb_info *fb; + struct jz4740_fb_platform_data *pdata = pdev->dev.platform_data; + struct resource *mem; + + if (!pdata) { + dev_err(&pdev->dev, "Missing platform data\n"); + return -ENOENT; + } + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + + if (!mem) { + dev_err(&pdev->dev, "Failed to get register memory resource\n"); + return -ENOENT; + } + + mem = request_mem_region(mem->start, resource_size(mem), pdev->name); + + if (!mem) { + dev_err(&pdev->dev, "Failed to request register memory region\n"); + return -EBUSY; + } + + + fb = framebuffer_alloc(sizeof(struct jzfb), &pdev->dev); + + if (!fb) { + dev_err(&pdev->dev, "Failed to allocate framebuffer device\n"); + ret = -ENOMEM; + goto err_release_mem_region; + } + + fb->fbops = &jzfb_ops; + fb->flags = FBINFO_DEFAULT; + + jzfb = fb->par; + jzfb->pdev = pdev; + jzfb->pdata = pdata; + jzfb->mem = mem; + + jzfb->ldclk = clk_get(&pdev->dev, "lcd"); + jzfb->lpclk = clk_get(&pdev->dev, "lcd_pclk"); + + jzfb->is_enabled = 1; + + if (IS_ERR(jzfb->ldclk)) { + ret = PTR_ERR(jzfb->ldclk); + dev_err(&pdev->dev, "Faild to get device clock: %d\n", ret); + goto err_framebuffer_release; + } + + if (IS_ERR(jzfb->lpclk)) { + ret = PTR_ERR(jzfb->ldclk); + dev_err(&pdev->dev, "Faild to get pixel clock: %d\n", ret); + goto err_framebuffer_release; + } + + + jzfb->base = ioremap(mem->start, resource_size(mem)); + + if (!jzfb->base) { + dev_err(&pdev->dev, "Failed to ioremap register memory region\n"); + ret = -EBUSY; + goto err_framebuffer_release; + } + + platform_set_drvdata(pdev, jzfb); + + fb_videomode_to_modelist(pdata->modes, pdata->num_modes, + &fb->modelist); + fb->mode = pdata->modes; + + fb_videomode_to_var(&fb->var, fb->mode); + fb->var.bits_per_pixel = pdata->bpp; + jzfb_check_var(&fb->var, fb); + + ret = jzfb_alloc_vidmem(jzfb); + if (ret) { + dev_err(&pdev->dev, "Failed to allocate video memory\n"); + goto err_iounmap; + } + + fb->fix = jzfb_fix; + fb->fix.line_length = fb->var.bits_per_pixel * fb->var.xres / 8; + fb->fix.mmio_start = mem->start; + fb->fix.mmio_len = resource_size(mem); + fb->fix.smem_start = jzfb->vidmem_phys; + fb->fix.smem_len = fb->fix.line_length * fb->var.yres; + fb->screen_base = jzfb->vidmem; + fb->pseudo_palette = jzfb->pseudo_palette; + + fb_alloc_cmap(&fb->cmap, 256, 0); + + jzfb_set_par(fb); + writel(jzfb->framedesc->next, jzfb->base + JZ_REG_LCD_DA0); + + jz_gpio_bulk_request(jz_lcd_pins, ARRAY_SIZE(jz_lcd_pins)); + + ret = register_framebuffer(fb); + if (ret) { + dev_err(&pdev->dev, "Failed to register framebuffer: %d\n", ret); + goto err_free_devmem; + } + + return 0; +err_free_devmem: + jzfb_free_devmem(jzfb); +err_iounmap: + iounmap(jzfb->base); +err_framebuffer_release: + framebuffer_release(fb); +err_release_mem_region: + release_mem_region(mem->start, resource_size(mem)); + return ret; +} + +static int __devexit jzfb_remove(struct platform_device *pdev) +{ + struct jzfb *jzfb = platform_get_drvdata(pdev); + + jz_gpio_bulk_free(jz_lcd_pins, ARRAY_SIZE(jz_lcd_pins)); + iounmap(jzfb->base); + release_mem_region(jzfb->mem->start, resource_size(jzfb->mem)); + jzfb_free_devmem(jzfb); + platform_set_drvdata(pdev, NULL); + framebuffer_release(jzfb->fb); + return 0; +} + +static struct platform_driver jzfb_driver = { + .probe = jzfb_probe, + .remove = __devexit_p(jzfb_remove), + + .driver = { + .name = "jz4740-fb", + }, +}; + +int __init jzfb_init(void) +{ + return platform_driver_register(&jzfb_driver); +} +module_init(jzfb_init); + +void __exit jzfb_exit(void) +{ + platform_driver_unregister(&jzfb_driver); +} +module_exit(jzfb_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("JZ4720/JZ4740 SoC LCD framebuffer driver"); +MODULE_ALIAS("platform:jz4740-fb"); +MODULE_ALIAS("platform:jz4720-fb"); -- cgit v1.1