diff options
Diffstat (limited to 'target/linux/brcm2708/patches-3.3')
9 files changed, 86173 insertions, 0 deletions
diff --git a/target/linux/brcm2708/patches-3.3/0001-Add-dwc_otg-driver.patch b/target/linux/brcm2708/patches-3.3/0001-Add-dwc_otg-driver.patch new file mode 100644 index 0000000000..2e7bcf03b1 --- /dev/null +++ b/target/linux/brcm2708/patches-3.3/0001-Add-dwc_otg-driver.patch @@ -0,0 +1,43253 @@ +From c2eaacd30565604bbf776ca6e83c9889ea87ea74 Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Tue, 17 Jan 2012 19:14:08 +0000 +Subject: [PATCH 1/7] Add dwc_otg driver + +Signed-off-by: popcornmix <popcornmix@gmail.com> +--- + drivers/usb/Makefile | 1 + + drivers/usb/core/generic.c | 1 + + drivers/usb/core/hub.c | 52 +- + drivers/usb/core/message.c | 79 + + drivers/usb/core/otg_whitelist.h | 172 +- + drivers/usb/gadget/Kconfig | 28 + + drivers/usb/gadget/file_storage.c | 70 +- + drivers/usb/host/Kconfig | 13 + + drivers/usb/host/Makefile | 2 + + drivers/usb/host/dwc_common_port/Makefile | 44 + + drivers/usb/host/dwc_common_port/Makefile.linux | 36 + + drivers/usb/host/dwc_common_port/doc/doxygen.cfg | 270 + + .../html/dir_c13d72e45af28cdc461a5f284d3d36fc.html | 81 + + .../usb/host/dwc_common_port/doc/html/dirs.html | 22 + + .../usb/host/dwc_common_port/doc/html/doxygen.css | 358 ++ + .../host/dwc_common_port/doc/html/dwc__cc_8h.html | 709 +++ + .../dwc_common_port/doc/html/dwc__crypto_8c.html | 435 ++ + .../dwc_common_port/doc/html/dwc__crypto_8h.html | 618 +++ + .../host/dwc_common_port/doc/html/dwc__dh_8h.html | 166 + + .../dwc_common_port/doc/html/dwc__list_8h.html | 1844 +++++++ + .../dwc_common_port/doc/html/dwc__modpow_8h.html | 48 + + .../dwc_common_port/doc/html/dwc__notifier_8h.html | 306 ++ + .../host/dwc_common_port/doc/html/dwc__os_8h.html | 3090 +++++++++++ + .../usb/host/dwc_common_port/doc/html/files.html | 34 + + .../usb/host/dwc_common_port/doc/html/globals.html | 163 + + .../dwc_common_port/doc/html/globals_defs.html | 41 + + .../dwc_common_port/doc/html/globals_func.html | 153 + + .../dwc_common_port/doc/html/globals_type.html | 41 + + .../usb/host/dwc_common_port/doc/html/index.html | 8 + + .../usb/host/dwc_common_port/doc/html/main.html | 45 + + .../usb/host/dwc_common_port/doc/html/pages.html | 23 + + drivers/usb/host/dwc_common_port/doc/html/tabs.css | 102 + + .../usb/host/dwc_common_port/doc/html/todo.html | 23 + + .../usb/host/dwc_common_port/doc/html/tree.html | 90 + + drivers/usb/host/dwc_common_port/dwc_cc.c | 506 ++ + drivers/usb/host/dwc_common_port/dwc_cc.h | 209 + + .../usb/host/dwc_common_port/dwc_common_linux.c | 1247 +++++ + drivers/usb/host/dwc_common_port/dwc_crypto.c | 306 ++ + drivers/usb/host/dwc_common_port/dwc_crypto.h | 103 + + drivers/usb/host/dwc_common_port/dwc_dh.c | 286 ++ + drivers/usb/host/dwc_common_port/dwc_dh.h | 98 + + drivers/usb/host/dwc_common_port/dwc_list.h | 616 +++ + drivers/usb/host/dwc_common_port/dwc_mem.c | 172 + + drivers/usb/host/dwc_common_port/dwc_modpow.c | 622 +++ + drivers/usb/host/dwc_common_port/dwc_modpow.h | 26 + + drivers/usb/host/dwc_common_port/dwc_notifier.c | 256 + + drivers/usb/host/dwc_common_port/dwc_notifier.h | 112 + + drivers/usb/host/dwc_common_port/dwc_os.h | 924 ++++ + drivers/usb/host/dwc_common_port/usb.h | 850 +++ + drivers/usb/host/dwc_otg/Makefile | 78 + + drivers/usb/host/dwc_otg/doc/doxygen.cfg | 224 + + drivers/usb/host/dwc_otg/doc/html/annotated.html | 120 + + drivers/usb/host/dwc_otg/doc/html/doxygen.css | 358 ++ + .../dwc_otg/doc/html/dummy__audio_8c-source.html | 1550 ++++++ + .../doc/html/dwc__cfi__common_8h-source.html | 115 + + .../host/dwc_otg/doc/html/dwc__cfi__common_8h.html | 119 + + .../dwc_otg/doc/html/dwc__otg__attr_8c-source.html | 828 +++ + .../host/dwc_otg/doc/html/dwc__otg__attr_8c.html | 485 ++ + .../dwc_otg/doc/html/dwc__otg__attr_8h-source.html | 105 + + .../host/dwc_otg/doc/html/dwc__otg__attr_8h.html | 116 + + .../dwc_otg/doc/html/dwc__otg__cfi_8c-source.html | 1724 +++++++ + .../host/dwc_otg/doc/html/dwc__otg__cfi_8c.html | 36 + + .../dwc_otg/doc/html/dwc__otg__cfi_8h-source.html | 299 ++ + .../host/dwc_otg/doc/html/dwc__otg__cfi_8h.html | 302 ++ + .../dwc_otg/doc/html/dwc__otg__cil_8c-source.html | 4922 ++++++++++++++++++ + .../host/dwc_otg/doc/html/dwc__otg__cil_8c.html | 3103 +++++++++++ + .../dwc_otg/doc/html/dwc__otg__cil_8h-source.html | 709 +++ + .../host/dwc_otg/doc/html/dwc__otg__cil_8h.html | 1844 +++++++ + .../doc/html/dwc__otg__cil__intr_8c-source.html | 742 +++ + .../dwc_otg/doc/html/dwc__otg__cil__intr_8c.html | 645 +++ + .../doc/html/dwc__otg__core__if_8h-source.html | 365 ++ + .../dwc_otg/doc/html/dwc__otg__core__if_8h.html | 1730 +++++++ + .../dwc_otg/doc/html/dwc__otg__dbg_8h-source.html | 100 + + .../host/dwc_otg/doc/html/dwc__otg__dbg_8h.html | 133 + + .../doc/html/dwc__otg__driver_8c-source.html | 1079 ++++ + .../host/dwc_otg/doc/html/dwc__otg__driver_8c.html | 719 +++ + .../doc/html/dwc__otg__driver_8h-source.html | 110 + + .../host/dwc_otg/doc/html/dwc__otg__driver_8h.html | 50 + + .../dwc_otg/doc/html/dwc__otg__hcd_8c-source.html | 2946 +++++++++++ + .../host/dwc_otg/doc/html/dwc__otg__hcd_8c.html | 1837 +++++++ + .../dwc_otg/doc/html/dwc__otg__hcd_8h-source.html | 517 ++ + .../host/dwc_otg/doc/html/dwc__otg__hcd_8h.html | 1310 +++++ + .../doc/html/dwc__otg__hcd__ddma_8c-source.html | 1070 ++++ + .../dwc_otg/doc/html/dwc__otg__hcd__ddma_8c.html | 311 ++ + .../doc/html/dwc__otg__hcd__if_8h-source.html | 191 + + .../dwc_otg/doc/html/dwc__otg__hcd__if_8h.html | 1381 +++++ + .../doc/html/dwc__otg__hcd__intr_8c-source.html | 1873 +++++++ + .../dwc_otg/doc/html/dwc__otg__hcd__intr_8c.html | 1252 +++++ + .../doc/html/dwc__otg__hcd__linux_8c-source.html | 726 +++ + .../dwc_otg/doc/html/dwc__otg__hcd__linux_8c.html | 514 ++ + .../doc/html/dwc__otg__hcd__queue_8c-source.html | 633 +++ + .../dwc_otg/doc/html/dwc__otg__hcd__queue_8c.html | 667 +++ + .../dwc_otg/doc/html/dwc__otg__pcd_8c-source.html | 1851 +++++++ + .../host/dwc_otg/doc/html/dwc__otg__pcd_8c.html | 1343 +++++ + .../dwc_otg/doc/html/dwc__otg__pcd_8h-source.html | 171 + + .../host/dwc_otg/doc/html/dwc__otg__pcd_8h.html | 254 + + .../doc/html/dwc__otg__pcd__if_8h-source.html | 174 + + .../dwc_otg/doc/html/dwc__otg__pcd__if_8h.html | 976 ++++ + .../doc/html/dwc__otg__pcd__intr_8c-source.html | 3629 +++++++++++++ + .../dwc_otg/doc/html/dwc__otg__pcd__intr_8c.html | 1599 ++++++ + .../doc/html/dwc__otg__pcd__linux_8c-source.html | 997 ++++ + .../dwc_otg/doc/html/dwc__otg__pcd__linux_8c.html | 796 +++ + .../dwc_otg/doc/html/dwc__otg__regs_8h-source.html | 1260 +++++ + .../host/dwc_otg/doc/html/dwc__otg__regs_8h.html | 1468 ++++++ + drivers/usb/host/dwc_otg/doc/html/files.html | 52 + + drivers/usb/host/dwc_otg/doc/html/functions.html | 82 + + .../usb/host/dwc_otg/doc/html/functions_0x62.html | 99 + + .../usb/host/dwc_otg/doc/html/functions_0x63.html | 110 + + .../usb/host/dwc_otg/doc/html/functions_0x64.html | 158 + + .../usb/host/dwc_otg/doc/html/functions_0x65.html | 109 + + .../usb/host/dwc_otg/doc/html/functions_0x66.html | 81 + + .../usb/host/dwc_otg/doc/html/functions_0x67.html | 95 + + .../usb/host/dwc_otg/doc/html/functions_0x68.html | 119 + + .../usb/host/dwc_otg/doc/html/functions_0x69.html | 121 + + .../usb/host/dwc_otg/doc/html/functions_0x6c.html | 74 + + .../usb/host/dwc_otg/doc/html/functions_0x6d.html | 79 + + .../usb/host/dwc_otg/doc/html/functions_0x6e.html | 99 + + .../usb/host/dwc_otg/doc/html/functions_0x6f.html | 101 + + .../usb/host/dwc_otg/doc/html/functions_0x70.html | 144 + + .../usb/host/dwc_otg/doc/html/functions_0x71.html | 71 + + .../usb/host/dwc_otg/doc/html/functions_0x72.html | 141 + + .../usb/host/dwc_otg/doc/html/functions_0x73.html | 128 + + .../usb/host/dwc_otg/doc/html/functions_0x74.html | 88 + + .../usb/host/dwc_otg/doc/html/functions_0x75.html | 78 + + .../usb/host/dwc_otg/doc/html/functions_0x76.html | 65 + + .../usb/host/dwc_otg/doc/html/functions_0x77.html | 79 + + .../usb/host/dwc_otg/doc/html/functions_0x78.html | 77 + + .../usb/host/dwc_otg/doc/html/functions_func.html | 36 + + .../usb/host/dwc_otg/doc/html/functions_vars.html | 82 + + .../host/dwc_otg/doc/html/functions_vars_0x62.html | 99 + + .../host/dwc_otg/doc/html/functions_vars_0x63.html | 110 + + .../host/dwc_otg/doc/html/functions_vars_0x64.html | 157 + + .../host/dwc_otg/doc/html/functions_vars_0x65.html | 109 + + .../host/dwc_otg/doc/html/functions_vars_0x66.html | 81 + + .../host/dwc_otg/doc/html/functions_vars_0x67.html | 95 + + .../host/dwc_otg/doc/html/functions_vars_0x68.html | 119 + + .../host/dwc_otg/doc/html/functions_vars_0x69.html | 121 + + .../host/dwc_otg/doc/html/functions_vars_0x6c.html | 74 + + .../host/dwc_otg/doc/html/functions_vars_0x6d.html | 79 + + .../host/dwc_otg/doc/html/functions_vars_0x6e.html | 99 + + .../host/dwc_otg/doc/html/functions_vars_0x6f.html | 101 + + .../host/dwc_otg/doc/html/functions_vars_0x70.html | 144 + + .../host/dwc_otg/doc/html/functions_vars_0x71.html | 71 + + .../host/dwc_otg/doc/html/functions_vars_0x72.html | 141 + + .../host/dwc_otg/doc/html/functions_vars_0x73.html | 128 + + .../host/dwc_otg/doc/html/functions_vars_0x74.html | 88 + + .../host/dwc_otg/doc/html/functions_vars_0x75.html | 78 + + .../host/dwc_otg/doc/html/functions_vars_0x76.html | 65 + + .../host/dwc_otg/doc/html/functions_vars_0x77.html | 79 + + .../host/dwc_otg/doc/html/functions_vars_0x78.html | 77 + + drivers/usb/host/dwc_otg/doc/html/globals.html | 87 + + .../usb/host/dwc_otg/doc/html/globals_0x61.html | 76 + + .../usb/host/dwc_otg/doc/html/globals_0x62.html | 83 + + .../usb/host/dwc_otg/doc/html/globals_0x63.html | 100 + + .../usb/host/dwc_otg/doc/html/globals_0x64.html | 686 +++ + .../usb/host/dwc_otg/doc/html/globals_0x65.html | 78 + + .../usb/host/dwc_otg/doc/html/globals_0x66.html | 87 + + .../usb/host/dwc_otg/doc/html/globals_0x67.html | 93 + + .../usb/host/dwc_otg/doc/html/globals_0x68.html | 129 + + .../usb/host/dwc_otg/doc/html/globals_0x69.html | 76 + + .../usb/host/dwc_otg/doc/html/globals_0x6b.html | 69 + + .../usb/host/dwc_otg/doc/html/globals_0x6d.html | 84 + + .../usb/host/dwc_otg/doc/html/globals_0x6e.html | 68 + + .../usb/host/dwc_otg/doc/html/globals_0x6f.html | 73 + + .../usb/host/dwc_otg/doc/html/globals_0x70.html | 84 + + .../usb/host/dwc_otg/doc/html/globals_0x71.html | 70 + + .../usb/host/dwc_otg/doc/html/globals_0x72.html | 94 + + .../usb/host/dwc_otg/doc/html/globals_0x73.html | 85 + + .../usb/host/dwc_otg/doc/html/globals_0x74.html | 68 + + .../usb/host/dwc_otg/doc/html/globals_0x75.html | 80 + + .../usb/host/dwc_otg/doc/html/globals_0x76.html | 75 + + .../usb/host/dwc_otg/doc/html/globals_0x77.html | 74 + + .../usb/host/dwc_otg/doc/html/globals_defs.html | 68 + + .../host/dwc_otg/doc/html/globals_defs_0x61.html | 64 + + .../host/dwc_otg/doc/html/globals_defs_0x62.html | 71 + + .../host/dwc_otg/doc/html/globals_defs_0x63.html | 74 + + .../host/dwc_otg/doc/html/globals_defs_0x64.html | 241 + + .../host/dwc_otg/doc/html/globals_defs_0x66.html | 71 + + .../host/dwc_otg/doc/html/globals_defs_0x67.html | 62 + + .../host/dwc_otg/doc/html/globals_defs_0x68.html | 63 + + .../host/dwc_otg/doc/html/globals_defs_0x69.html | 62 + + .../host/dwc_otg/doc/html/globals_defs_0x6d.html | 76 + + .../host/dwc_otg/doc/html/globals_defs_0x6e.html | 62 + + .../host/dwc_otg/doc/html/globals_defs_0x6f.html | 67 + + .../host/dwc_otg/doc/html/globals_defs_0x72.html | 66 + + .../host/dwc_otg/doc/html/globals_defs_0x73.html | 63 + + .../host/dwc_otg/doc/html/globals_defs_0x75.html | 64 + + .../host/dwc_otg/doc/html/globals_defs_0x76.html | 68 + + .../usb/host/dwc_otg/doc/html/globals_enum.html | 44 + + .../usb/host/dwc_otg/doc/html/globals_eval.html | 43 + + .../usb/host/dwc_otg/doc/html/globals_func.html | 77 + + .../host/dwc_otg/doc/html/globals_func_0x61.html | 70 + + .../host/dwc_otg/doc/html/globals_func_0x62.html | 68 + + .../host/dwc_otg/doc/html/globals_func_0x63.html | 77 + + .../host/dwc_otg/doc/html/globals_func_0x64.html | 427 ++ + .../host/dwc_otg/doc/html/globals_func_0x65.html | 73 + + .../host/dwc_otg/doc/html/globals_func_0x66.html | 72 + + .../host/dwc_otg/doc/html/globals_func_0x67.html | 78 + + .../host/dwc_otg/doc/html/globals_func_0x68.html | 101 + + .../host/dwc_otg/doc/html/globals_func_0x69.html | 71 + + .../host/dwc_otg/doc/html/globals_func_0x6b.html | 66 + + .../host/dwc_otg/doc/html/globals_func_0x6d.html | 66 + + .../host/dwc_otg/doc/html/globals_func_0x70.html | 78 + + .../host/dwc_otg/doc/html/globals_func_0x71.html | 67 + + .../host/dwc_otg/doc/html/globals_func_0x72.html | 80 + + .../host/dwc_otg/doc/html/globals_func_0x73.html | 77 + + .../host/dwc_otg/doc/html/globals_func_0x75.html | 73 + + .../host/dwc_otg/doc/html/globals_func_0x76.html | 65 + + .../host/dwc_otg/doc/html/globals_func_0x77.html | 70 + + .../usb/host/dwc_otg/doc/html/globals_type.html | 175 + + .../usb/host/dwc_otg/doc/html/globals_vars.html | 120 + + drivers/usb/host/dwc_otg/doc/html/index.html | 8 + + .../dwc_otg/doc/html/linux module attributes.html | 130 + + drivers/usb/host/dwc_otg/doc/html/main.html | 21 + + .../host/dwc_otg/doc/html/module parameters.html | 189 + + drivers/usb/host/dwc_otg/doc/html/pages.html | 27 + + .../html/struct__ddma__align__buffer__setup.html | 46 + + .../html/struct__ddma__concat__buffer__setup.html | 46 + + .../struct__ddma__concat__buffer__setup__hdr.html | 49 + + .../doc/html/struct__ddma__sg__buffer__setup.html | 57 + + .../doc/html/struct__rx__fifo__size__setup.html | 43 + + .../doc/html/struct__tx__fifo__size__setup.html | 46 + + .../doc/html/structcfi__all__features__header.html | 75 + + .../dwc_otg/doc/html/structcfi__dma__buff.html | 41 + + .../usb/host/dwc_otg/doc/html/structcfi__ep.html | 69 + + .../doc/html/structcfi__feature__desc__header.html | 60 + + .../usb/host/dwc_otg/doc/html/structcfi__ops.html | 64 + + .../host/dwc_otg/doc/html/structcfi__string.html | 48 + + .../doc/html/structcfi__usb__ctrlrequest.html | 58 + + .../usb/host/dwc_otg/doc/html/structcfiobject.html | 62 + + .../usb/host/dwc_otg/doc/html/structdwc__ep.html | 192 + + .../usb/host/dwc_otg/doc/html/structdwc__hc.html | 345 ++ + .../doc/html/structdwc__otg__cil__callbacks.html | 70 + + .../html/structdwc__otg__core__global__regs.html | 557 ++ + .../dwc_otg/doc/html/structdwc__otg__core__if.html | 190 + + .../doc/html/structdwc__otg__core__params.html | 606 +++ + .../doc/html/structdwc__otg__dev__dma__desc.html | 50 + + .../html/structdwc__otg__dev__global__regs.html | 441 ++ + .../dwc_otg/doc/html/structdwc__otg__dev__if.html | 142 + + .../html/structdwc__otg__dev__in__ep__regs.html | 221 + + .../html/structdwc__otg__dev__out__ep__regs.html | 221 + + .../dwc_otg/doc/html/structdwc__otg__device.html | 64 + + .../structdwc__otg__driver__module__params.html | 146 + + .../dwc_otg/doc/html/structdwc__otg__hc__regs.html | 200 + + .../host/dwc_otg/doc/html/structdwc__otg__hcd.html | 377 ++ + .../html/structdwc__otg__hcd__function__ops.html | 53 + + .../structdwc__otg__hcd__iso__packet__desc.html | 47 + + .../doc/html/structdwc__otg__hcd__pipe__info.html | 50 + + .../dwc_otg/doc/html/structdwc__otg__hcd__urb.html | 80 + + .../doc/html/structdwc__otg__host__dma__desc.html | 50 + + .../html/structdwc__otg__host__global__regs.html | 219 + + .../dwc_otg/doc/html/structdwc__otg__host__if.html | 66 + + .../host/dwc_otg/doc/html/structdwc__otg__pcd.html | 152 + + .../dwc_otg/doc/html/structdwc__otg__pcd__ep.html | 77 + + .../html/structdwc__otg__pcd__function__ops.html | 73 + + .../doc/html/structdwc__otg__pcd__request.html | 64 + + .../host/dwc_otg/doc/html/structdwc__otg__qh.html | 228 + + .../host/dwc_otg/doc/html/structdwc__otg__qtd.html | 157 + + .../dwc_otg/doc/html/structgadget__wrapper.html | 53 + + .../dwc_otg/doc/html/structiso__pkt__info.html | 49 + + .../doc/html/structwrapper__priv__data.html | 38 + + .../usb/host/dwc_otg/doc/html/structzero__dev.html | 56 + + drivers/usb/host/dwc_otg/doc/html/tabs.css | 102 + + drivers/usb/host/dwc_otg/doc/html/todo.html | 262 + + drivers/usb/host/dwc_otg/doc/html/tree.html | 201 + + .../host/dwc_otg/doc/html/uniondaint__data.html | 131 + + .../usb/host/dwc_otg/doc/html/uniondcfg__data.html | 74 + + .../usb/host/dwc_otg/doc/html/uniondctl__data.html | 96 + + .../host/dwc_otg/doc/html/uniondepctl__data.html | 139 + + .../host/dwc_otg/doc/html/uniondeptsiz0__data.html | 69 + + .../host/dwc_otg/doc/html/uniondeptsiz__data.html | 63 + + .../dwc_otg/doc/html/uniondev__dma__desc__sts.html | 140 + + .../doc/html/uniondevice__grxsts__data.html | 62 + + .../host/dwc_otg/doc/html/uniondiepint__data.html | 90 + + .../host/dwc_otg/doc/html/uniondoepint__data.html | 98 + + .../usb/host/dwc_otg/doc/html/uniondsts__data.html | 68 + + .../host/dwc_otg/doc/html/uniondthrctl__data.html | 178 + + .../host/dwc_otg/doc/html/uniondtknq1__data.html | 86 + + .../host/dwc_otg/doc/html/uniondtxfsts__data.html | 56 + + ...otg__hcd_1_1dwc__otg__hcd__internal__flags.html | 66 + + .../host/dwc_otg/doc/html/unionfifosize__data.html | 56 + + .../host/dwc_otg/doc/html/uniongahbcfg__data.html | 66 + + .../host/dwc_otg/doc/html/uniongi2cctl__data.html | 72 + + .../host/dwc_otg/doc/html/uniongintmsk__data.html | 114 + + .../host/dwc_otg/doc/html/uniongintsts__data.html | 114 + + .../host/dwc_otg/doc/html/unionglpmctl__data.html | 178 + + .../host/dwc_otg/doc/html/uniongnptxsts__data.html | 69 + + .../host/dwc_otg/doc/html/uniongotgctl__data.html | 80 + + .../host/dwc_otg/doc/html/uniongotgint__data.html | 79 + + .../host/dwc_otg/doc/html/uniongrstctl__data.html | 249 + + .../host/dwc_otg/doc/html/uniongusbcfg__data.html | 98 + + .../host/dwc_otg/doc/html/unionhaint__data.html | 93 + + .../host/dwc_otg/doc/html/unionhaintmsk__data.html | 93 + + .../host/dwc_otg/doc/html/unionhcchar__data.html | 123 + + .../host/dwc_otg/doc/html/unionhcdma__data.html | 78 + + .../usb/host/dwc_otg/doc/html/unionhcfg__data.html | 72 + + .../host/dwc_otg/doc/html/unionhcint__data.html | 95 + + .../host/dwc_otg/doc/html/unionhcintmsk__data.html | 82 + + .../host/dwc_otg/doc/html/unionhcsplt__data.html | 63 + + .../host/dwc_otg/doc/html/unionhctsiz__data.html | 105 + + .../usb/host/dwc_otg/doc/html/unionhfir__data.html | 54 + + .../host/dwc_otg/doc/html/unionhfnum__data.html | 54 + + .../doc/html/unionhost__dma__desc__sts.html | 123 + + .../dwc_otg/doc/html/unionhost__grxsts__data.html | 60 + + .../host/dwc_otg/doc/html/unionhprt0__data.html | 82 + + .../host/dwc_otg/doc/html/unionhptxsts__data.html | 62 + + .../host/dwc_otg/doc/html/unionhwcfg1__data.html | 84 + + .../host/dwc_otg/doc/html/unionhwcfg2__data.html | 82 + + .../host/dwc_otg/doc/html/unionhwcfg3__data.html | 76 + + .../host/dwc_otg/doc/html/unionhwcfg4__data.html | 80 + + .../host/dwc_otg/doc/html/unionpcgcctl__data.html | 78 + + drivers/usb/host/dwc_otg/dummy_audio.c | 1575 ++++++ + drivers/usb/host/dwc_otg/dwc_cfi_common.h | 142 + + drivers/usb/host/dwc_otg/dwc_otg_attr.c | 1316 +++++ + drivers/usb/host/dwc_otg/dwc_otg_attr.h | 88 + + drivers/usb/host/dwc_otg/dwc_otg_cfi.c | 1876 +++++++ + drivers/usb/host/dwc_otg/dwc_otg_cfi.h | 319 ++ + drivers/usb/host/dwc_otg/dwc_otg_cil.c | 5410 ++++++++++++++++++++ + drivers/usb/host/dwc_otg/dwc_otg_cil.h | 1143 +++++ + drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c | 846 +++ + drivers/usb/host/dwc_otg/dwc_otg_core_if.h | 641 +++ + drivers/usb/host/dwc_otg/dwc_otg_dbg.h | 113 + + drivers/usb/host/dwc_otg/dwc_otg_driver.c | 1577 ++++++ + drivers/usb/host/dwc_otg/dwc_otg_driver.h | 101 + + drivers/usb/host/dwc_otg/dwc_otg_hcd.c | 3330 ++++++++++++ + drivers/usb/host/dwc_otg/dwc_otg_hcd.h | 804 +++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c | 1106 ++++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h | 393 ++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c | 2065 ++++++++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c | 840 +++ + drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c | 732 +++ + drivers/usb/host/dwc_otg/dwc_otg_pcd.c | 2067 ++++++++ + drivers/usb/host/dwc_otg/dwc_otg_pcd.h | 216 + + drivers/usb/host/dwc_otg/dwc_otg_pcd_if.h | 333 ++ + drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c | 4077 +++++++++++++++ + drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c | 1288 +++++ + drivers/usb/host/dwc_otg/dwc_otg_regs.h | 2237 ++++++++ + drivers/usb/host/dwc_otg/test/Makefile | 16 + + drivers/usb/host/dwc_otg/test/dwc_otg_test.pm | 337 ++ + drivers/usb/host/dwc_otg/test/test_mod_param.pl | 133 + + drivers/usb/host/dwc_otg/test/test_sysfs.pl | 193 + + 341 files changed, 124762 insertions(+), 59 deletions(-) + create mode 100644 drivers/usb/host/dwc_common_port/Makefile + create mode 100644 drivers/usb/host/dwc_common_port/Makefile.linux + create mode 100644 drivers/usb/host/dwc_common_port/doc/doxygen.cfg + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/dir_c13d72e45af28cdc461a5f284d3d36fc.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/dirs.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/doxygen.css + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/dwc__cc_8h.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/dwc__crypto_8c.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/dwc__crypto_8h.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/dwc__dh_8h.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/dwc__list_8h.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/dwc__modpow_8h.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/dwc__notifier_8h.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/dwc__os_8h.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/files.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/globals.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/globals_defs.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/globals_func.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/globals_type.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/index.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/main.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/pages.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/tabs.css + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/todo.html + create mode 100644 drivers/usb/host/dwc_common_port/doc/html/tree.html + create mode 100644 drivers/usb/host/dwc_common_port/dwc_cc.c + create mode 100644 drivers/usb/host/dwc_common_port/dwc_cc.h + create mode 100644 drivers/usb/host/dwc_common_port/dwc_common_linux.c + create mode 100644 drivers/usb/host/dwc_common_port/dwc_crypto.c + create mode 100644 drivers/usb/host/dwc_common_port/dwc_crypto.h + create mode 100644 drivers/usb/host/dwc_common_port/dwc_dh.c + create mode 100644 drivers/usb/host/dwc_common_port/dwc_dh.h + create mode 100644 drivers/usb/host/dwc_common_port/dwc_list.h + create mode 100644 drivers/usb/host/dwc_common_port/dwc_mem.c + create mode 100644 drivers/usb/host/dwc_common_port/dwc_modpow.c + create mode 100644 drivers/usb/host/dwc_common_port/dwc_modpow.h + create mode 100644 drivers/usb/host/dwc_common_port/dwc_notifier.c + create mode 100644 drivers/usb/host/dwc_common_port/dwc_notifier.h + create mode 100644 drivers/usb/host/dwc_common_port/dwc_os.h + create mode 100644 drivers/usb/host/dwc_common_port/usb.h + create mode 100644 drivers/usb/host/dwc_otg/Makefile + create mode 100644 drivers/usb/host/dwc_otg/doc/doxygen.cfg + create mode 100644 drivers/usb/host/dwc_otg/doc/html/annotated.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/doxygen.css + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dummy__audio_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__cfi__common_8h-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__cfi__common_8h.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__attr_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__attr_8c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__attr_8h-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__attr_8h.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__cfi_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__cfi_8c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__cfi_8h-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__cfi_8h.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__cil_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__cil_8c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__cil_8h-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__cil_8h.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__cil__intr_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__cil__intr_8c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__core__if_8h-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__core__if_8h.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__dbg_8h-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__dbg_8h.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__driver_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__driver_8c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__driver_8h-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__driver_8h.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd_8c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd_8h-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd_8h.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd__ddma_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd__ddma_8c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd__if_8h-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd__if_8h.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd__intr_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd__intr_8c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd__linux_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd__linux_8c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd__queue_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__hcd__queue_8c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__pcd_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__pcd_8c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__pcd_8h-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__pcd_8h.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__pcd__if_8h-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__pcd__if_8h.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__pcd__intr_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__pcd__intr_8c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__pcd__linux_8c-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__pcd__linux_8c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__regs_8h-source.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/dwc__otg__regs_8h.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/files.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x62.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x63.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x64.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x65.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x66.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x67.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x68.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x69.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x6c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x6d.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x6e.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x6f.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x70.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x71.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x72.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x73.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x74.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x75.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x76.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x77.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_0x78.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_func.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x62.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x63.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x64.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x65.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x66.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x67.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x68.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x69.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x6c.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x6d.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x6e.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x6f.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x70.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x71.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x72.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x73.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x74.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x75.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x76.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x77.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/functions_vars_0x78.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x61.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x62.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x63.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x64.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x65.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x66.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x67.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x68.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x69.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x6b.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x6d.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x6e.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x6f.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x70.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x71.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x72.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x73.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x74.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x75.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x76.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_0x77.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x61.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x62.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x63.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x64.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x66.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x67.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x68.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x69.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x6d.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x6e.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x6f.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x72.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x73.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x75.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_defs_0x76.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_enum.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_eval.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x61.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x62.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x63.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x64.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x65.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x66.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x67.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x68.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x69.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x6b.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x6d.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x70.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x71.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x72.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x73.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x75.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x76.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_func_0x77.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_type.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/globals_vars.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/index.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/linux module attributes.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/main.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/module parameters.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/pages.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/struct__ddma__align__buffer__setup.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/struct__ddma__concat__buffer__setup.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/struct__ddma__concat__buffer__setup__hdr.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/struct__ddma__sg__buffer__setup.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/struct__rx__fifo__size__setup.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/struct__tx__fifo__size__setup.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structcfi__all__features__header.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structcfi__dma__buff.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structcfi__ep.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structcfi__feature__desc__header.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structcfi__ops.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structcfi__string.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structcfi__usb__ctrlrequest.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structcfiobject.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__ep.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__hc.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__cil__callbacks.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__core__global__regs.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__core__if.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__core__params.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__dev__dma__desc.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__dev__global__regs.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__dev__if.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__dev__in__ep__regs.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__dev__out__ep__regs.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__device.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__driver__module__params.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__hc__regs.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__hcd.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__hcd__function__ops.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__hcd__iso__packet__desc.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__hcd__pipe__info.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__hcd__urb.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__host__dma__desc.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__host__global__regs.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__host__if.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__pcd.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__pcd__ep.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__pcd__function__ops.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__pcd__request.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__qh.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structdwc__otg__qtd.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structgadget__wrapper.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structiso__pkt__info.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structwrapper__priv__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/structzero__dev.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/tabs.css + create mode 100644 drivers/usb/host/dwc_otg/doc/html/todo.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/tree.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondaint__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondcfg__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondctl__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondepctl__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondeptsiz0__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondeptsiz__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondev__dma__desc__sts.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondevice__grxsts__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondiepint__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondoepint__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondsts__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondthrctl__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondtknq1__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondtxfsts__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniondwc__otg__hcd_1_1dwc__otg__hcd__internal__flags.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionfifosize__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniongahbcfg__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniongi2cctl__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniongintmsk__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniongintsts__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionglpmctl__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniongnptxsts__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniongotgctl__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniongotgint__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniongrstctl__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/uniongusbcfg__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhaint__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhaintmsk__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhcchar__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhcdma__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhcfg__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhcint__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhcintmsk__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhcsplt__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhctsiz__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhfir__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhfnum__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhost__dma__desc__sts.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhost__grxsts__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhprt0__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhptxsts__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhwcfg1__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhwcfg2__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhwcfg3__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionhwcfg4__data.html + create mode 100644 drivers/usb/host/dwc_otg/doc/html/unionpcgcctl__data.html + create mode 100644 drivers/usb/host/dwc_otg/dummy_audio.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_cfi_common.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_attr.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_attr.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_cfi.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_cfi.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_cil.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_cil.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_core_if.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_dbg.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_driver.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_driver.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_pcd.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_pcd.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_pcd_if.h + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c + create mode 100644 drivers/usb/host/dwc_otg/dwc_otg_regs.h + create mode 100644 drivers/usb/host/dwc_otg/test/Makefile + create mode 100644 drivers/usb/host/dwc_otg/test/dwc_otg_test.pm + create mode 100644 drivers/usb/host/dwc_otg/test/test_mod_param.pl + create mode 100644 drivers/usb/host/dwc_otg/test/test_sysfs.pl + +--- a/drivers/usb/Makefile ++++ b/drivers/usb/Makefile +@@ -25,6 +25,7 @@ obj-$(CONFIG_USB_U132_HCD) += host/ + obj-$(CONFIG_USB_R8A66597_HCD) += host/ + obj-$(CONFIG_USB_HWA_HCD) += host/ + obj-$(CONFIG_USB_ISP1760_HCD) += host/ ++obj-$(CONFIG_USB_DWCOTG) += host/ + obj-$(CONFIG_USB_IMX21_HCD) += host/ + obj-$(CONFIG_USB_FSL_MPH_DR_OF) += host/ + +--- a/drivers/usb/core/generic.c ++++ b/drivers/usb/core/generic.c +@@ -149,6 +149,7 @@ int usb_choose_configuration(struct usb_ + dev_warn(&udev->dev, + "no configuration chosen from %d choice%s\n", + num_configs, plural(num_configs)); ++ dev_warn(&udev->dev, "No support over %dmA\n", udev->bus_mA); + } + return i; + } +--- a/drivers/usb/core/hub.c ++++ b/drivers/usb/core/hub.c +@@ -1075,6 +1075,8 @@ static int hub_configure(struct usb_hub + INIT_WORK(&hub->tt.clear_work, hub_tt_work); + switch (hdev->descriptor.bDeviceProtocol) { + case USB_HUB_PR_FS: ++ dev_dbg(hub_dev, "TT with no hub-specific protocol - " ++ "no TT\n"); + break; + case USB_HUB_PR_HS_SINGLE_TT: + dev_dbg(hub_dev, "Single TT\n"); +@@ -1091,6 +1093,7 @@ static int hub_configure(struct usb_hub + hub->tt.hub = hdev; + break; + case USB_HUB_PR_SS: ++ dev_dbg(hub_dev, "USB 3.0 hub - no TT\n"); + /* USB 3.0 hubs don't have a TT */ + break; + default: +@@ -1719,6 +1722,12 @@ static inline void announce_device(struc + #endif + + #ifdef CONFIG_USB_OTG ++ ++static int enable_whitelist; ++module_param(enable_whitelist, bool, S_IRUGO | S_IWUSR); ++MODULE_PARM_DESC(enable_whitelist, ++ "only recognize devices in OTG whitelist if true"); ++ + #include "otg_whitelist.h" + #endif + +@@ -1773,9 +1782,15 @@ static int usb_enumerate_device_otg(stru + dev_info(&udev->dev, + "can't set HNP mode: %d\n", + err); ++ dev_printk(KERN_CRIT, &udev->dev, ++ "Not Connected/Responding\n"); ++ + bus->b_hnp_enable = 0; ++ } else { ++ dev_info(&udev->dev, ++ "HNP Not Supported\n"); + } +- } ++ } + } + } + +@@ -1784,12 +1799,27 @@ static int usb_enumerate_device_otg(stru + /* Maybe it can talk to us, though we can't talk to it. + * (Includes HNP test device.) + */ +- if (udev->bus->b_hnp_enable || udev->bus->is_b_host) { ++ if (udev->bus->b_hnp_enable || udev->bus->is_b_host || ++ udev->descriptor.idVendor == 0x1a0a) { + err = usb_port_suspend(udev, PMSG_SUSPEND); +- if (err < 0) ++ if (err < 0) { + dev_dbg(&udev->dev, "HNP fail, %d\n", err); ++ } else { ++ /* Return Connection Refused(ECONNREFUSED) ++ * instead of No Device(ENODEV) so that the ++ * retry loop in hub_port_connect_change() is ++ * exited without disabling the port ++ */ ++ err = -ECONNREFUSED; ++ goto fail; ++ } + } +- err = -ENOTSUPP; ++ //err = -ENOTSUPP; ++ /* Return Not Connected (ENOTCONN) instead of No ++ * Device(ENODEV) so that the retry loop in ++ * hub_port_connect_change() is exited ++ */ ++ err = -ENOTCONN; + goto fail; + } + fail: +@@ -2980,7 +3010,9 @@ hub_port_init (struct usb_hub *hub, stru + buf->bMaxPacketSize0 = 0; + r = usb_control_msg(udev, usb_rcvaddr0pipe(), + USB_REQ_GET_DESCRIPTOR, USB_DIR_IN, +- USB_DT_DEVICE << 8, 0, ++ USB_DT_DEVICE << 8, ++ //USB_DT_DEVICE << 64, // DWC patch suggestion! ++ 0, + buf, GET_DESCRIPTOR_BUFSIZE, + initial_descriptor_timeout); + switch (buf->bMaxPacketSize0) { +@@ -3426,8 +3458,10 @@ loop: + release_devnum(udev); + hub_free_dev(udev); + usb_put_dev(udev); +- if ((status == -ENOTCONN) || (status == -ENOTSUPP)) +- break; ++ if (status == -ENOTCONN || status == -ENOTSUPP || ++ status == -ECONNREFUSED) ++ // break; //DWC patch ++ return; + } + if (hub->hdev->parent || + !hcd->driver->port_handed_over || +--- a/drivers/usb/core/message.c ++++ b/drivers/usb/core/message.c +@@ -1837,6 +1837,85 @@ free_interfaces: + if (cp->string == NULL && + !(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS)) + cp->string = usb_cache_string(dev, cp->desc.iConfiguration); ++/* Uncomment this define to enable the HS Electrical Test support */ ++#define DWC_HS_ELECT_TST 1 ++#ifdef DWC_HS_ELECT_TST ++ /* Here we implement the HS Electrical Test support. The ++ * tester uses a vendor ID of 0x1A0A to indicate we should ++ * run a special test sequence. The product ID tells us ++ * which sequence to run. We invoke the test sequence by ++ * sending a non-standard SetFeature command to our root ++ * hub port. Our dwc_otg_hcd_hub_control() routine will ++ * recognize the command and perform the desired test ++ * sequence. ++ */ ++ if (dev->descriptor.idVendor == 0x1A0A) { ++ /* HSOTG Electrical Test */ ++ dev_warn(&dev->dev, "VID from HSOTG Electrical Test Fixture\n"); ++ ++ if (dev->bus && dev->bus->root_hub) { ++ struct usb_device *hdev = dev->bus->root_hub; ++ dev_warn(&dev->dev, "Got PID 0x%x\n", dev->descriptor.idProduct); ++ ++ switch (dev->descriptor.idProduct) { ++ case 0x0101: /* TEST_SE0_NAK */ ++ dev_warn(&dev->dev, "TEST_SE0_NAK\n"); ++ usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), ++ USB_REQ_SET_FEATURE, USB_RT_PORT, ++ USB_PORT_FEAT_TEST, 0x300, NULL, 0, HZ); ++ break; ++ ++ case 0x0102: /* TEST_J */ ++ dev_warn(&dev->dev, "TEST_J\n"); ++ usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), ++ USB_REQ_SET_FEATURE, USB_RT_PORT, ++ USB_PORT_FEAT_TEST, 0x100, NULL, 0, HZ); ++ break; ++ ++ case 0x0103: /* TEST_K */ ++ dev_warn(&dev->dev, "TEST_K\n"); ++ usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), ++ USB_REQ_SET_FEATURE, USB_RT_PORT, ++ USB_PORT_FEAT_TEST, 0x200, NULL, 0, HZ); ++ break; ++ ++ case 0x0104: /* TEST_PACKET */ ++ dev_warn(&dev->dev, "TEST_PACKET\n"); ++ usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), ++ USB_REQ_SET_FEATURE, USB_RT_PORT, ++ USB_PORT_FEAT_TEST, 0x400, NULL, 0, HZ); ++ break; ++ ++ case 0x0105: /* TEST_FORCE_ENABLE */ ++ dev_warn(&dev->dev, "TEST_FORCE_ENABLE\n"); ++ usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), ++ USB_REQ_SET_FEATURE, USB_RT_PORT, ++ USB_PORT_FEAT_TEST, 0x500, NULL, 0, HZ); ++ break; ++ ++ case 0x0106: /* HS_HOST_PORT_SUSPEND_RESUME */ ++ dev_warn(&dev->dev, "HS_HOST_PORT_SUSPEND_RESUME\n"); ++ usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), ++ USB_REQ_SET_FEATURE, USB_RT_PORT, ++ USB_PORT_FEAT_TEST, 0x600, NULL, 0, 40 * HZ); ++ break; ++ ++ case 0x0107: /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */ ++ dev_warn(&dev->dev, "SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup\n"); ++ usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), ++ USB_REQ_SET_FEATURE, USB_RT_PORT, ++ USB_PORT_FEAT_TEST, 0x700, NULL, 0, 40 * HZ); ++ break; ++ ++ case 0x0108: /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */ ++ dev_warn(&dev->dev, "SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute\n"); ++ usb_control_msg(hdev, usb_sndctrlpipe(hdev, 0), ++ USB_REQ_SET_FEATURE, USB_RT_PORT, ++ USB_PORT_FEAT_TEST, 0x800, NULL, 0, 40 * HZ); ++ } ++ } ++ } ++#endif /* DWC_HS_ELECT_TST */ + + /* Now that all the interfaces are set up, register them + * to trigger binding of drivers to interfaces. probe() +--- a/drivers/usb/core/otg_whitelist.h ++++ b/drivers/usb/core/otg_whitelist.h +@@ -19,33 +19,82 @@ + static struct usb_device_id whitelist_table [] = { + + /* hubs are optional in OTG, but very handy ... */ ++#define CERT_WITHOUT_HUBS ++#if defined(CERT_WITHOUT_HUBS) ++{ USB_DEVICE( 0x0000, 0x0000 ), }, /* Root HUB Only*/ ++#else + { USB_DEVICE_INFO(USB_CLASS_HUB, 0, 0), }, + { USB_DEVICE_INFO(USB_CLASS_HUB, 0, 1), }, ++{ USB_DEVICE_INFO(USB_CLASS_HUB, 0, 2), }, ++#endif + + #ifdef CONFIG_USB_PRINTER /* ignoring nonstatic linkage! */ + /* FIXME actually, printers are NOT supposed to use device classes; + * they're supposed to use interface classes... + */ +-{ USB_DEVICE_INFO(7, 1, 1) }, +-{ USB_DEVICE_INFO(7, 1, 2) }, +-{ USB_DEVICE_INFO(7, 1, 3) }, ++//{ USB_DEVICE_INFO(7, 1, 1) }, ++//{ USB_DEVICE_INFO(7, 1, 2) }, ++//{ USB_DEVICE_INFO(7, 1, 3) }, + #endif + + #ifdef CONFIG_USB_NET_CDCETHER + /* Linux-USB CDC Ethernet gadget */ +-{ USB_DEVICE(0x0525, 0xa4a1), }, ++//{ USB_DEVICE(0x0525, 0xa4a1), }, + /* Linux-USB CDC Ethernet + RNDIS gadget */ +-{ USB_DEVICE(0x0525, 0xa4a2), }, ++//{ USB_DEVICE(0x0525, 0xa4a2), }, + #endif + + #if defined(CONFIG_USB_TEST) || defined(CONFIG_USB_TEST_MODULE) + /* gadget zero, for testing */ +-{ USB_DEVICE(0x0525, 0xa4a0), }, ++//{ USB_DEVICE(0x0525, 0xa4a0), }, + #endif ++ ++/* OPT Tester */ ++{ USB_DEVICE( 0x1a0a, 0x0101 ), }, /* TEST_SE0_NAK */ ++{ USB_DEVICE( 0x1a0a, 0x0102 ), }, /* Test_J */ ++{ USB_DEVICE( 0x1a0a, 0x0103 ), }, /* Test_K */ ++{ USB_DEVICE( 0x1a0a, 0x0104 ), }, /* Test_PACKET */ ++{ USB_DEVICE( 0x1a0a, 0x0105 ), }, /* Test_FORCE_ENABLE */ ++{ USB_DEVICE( 0x1a0a, 0x0106 ), }, /* HS_PORT_SUSPEND_RESUME */ ++{ USB_DEVICE( 0x1a0a, 0x0107 ), }, /* SINGLE_STEP_GET_DESCRIPTOR setup */ ++{ USB_DEVICE( 0x1a0a, 0x0108 ), }, /* SINGLE_STEP_GET_DESCRIPTOR execute */ ++ ++/* Sony cameras */ ++{ USB_DEVICE_VER(0x054c,0x0010,0x0410, 0x0500), }, ++ ++/* Memory Devices */ ++//{ USB_DEVICE( 0x0781, 0x5150 ), }, /* SanDisk */ ++//{ USB_DEVICE( 0x05DC, 0x0080 ), }, /* Lexar */ ++//{ USB_DEVICE( 0x4146, 0x9281 ), }, /* IOMEGA */ ++//{ USB_DEVICE( 0x067b, 0x2507 ), }, /* Hammer 20GB External HD */ ++{ USB_DEVICE( 0x0EA0, 0x2168 ), }, /* Ours Technology Inc. (BUFFALO ClipDrive)*/ ++//{ USB_DEVICE( 0x0457, 0x0150 ), }, /* Silicon Integrated Systems Corp. */ ++ ++/* HP Printers */ ++//{ USB_DEVICE( 0x03F0, 0x1102 ), }, /* HP Photosmart 245 */ ++//{ USB_DEVICE( 0x03F0, 0x1302 ), }, /* HP Photosmart 370 Series */ ++ ++/* Speakers */ ++//{ USB_DEVICE( 0x0499, 0x3002 ), }, /* YAMAHA YST-MS35D USB Speakers */ ++//{ USB_DEVICE( 0x0672, 0x1041 ), }, /* Labtec USB Headset */ + + { } /* Terminating entry */ + }; + ++static inline void report_errors(struct usb_device *dev) ++{ ++ /* OTG MESSAGE: report errors here, customize to match your product */ ++ dev_info(&dev->dev, "device Vendor:%04x Product:%04x is not supported\n", ++ le16_to_cpu(dev->descriptor.idVendor), ++ le16_to_cpu(dev->descriptor.idProduct)); ++ if (USB_CLASS_HUB == dev->descriptor.bDeviceClass){ ++ dev_printk(KERN_CRIT, &dev->dev, "Unsupported Hub Topology\n"); ++ } else { ++ dev_printk(KERN_CRIT, &dev->dev, "Attached Device is not Supported\n"); ++ } ++} ++ ++ + static int is_targeted(struct usb_device *dev) + { + struct usb_device_id *id = whitelist_table; +@@ -55,58 +104,83 @@ static int is_targeted(struct usb_device + return 1; + + /* HNP test device is _never_ targeted (see OTG spec 6.6.6) */ +- if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1a0a && +- le16_to_cpu(dev->descriptor.idProduct) == 0xbadd)) +- return 0; ++ if (dev->descriptor.idVendor == 0x1a0a && ++ dev->descriptor.idProduct == 0xbadd) { ++ return 0; ++ } else if (!enable_whitelist) { ++ return 1; ++ } else { + +- /* NOTE: can't use usb_match_id() since interface caches +- * aren't set up yet. this is cut/paste from that code. +- */ +- for (id = whitelist_table; id->match_flags; id++) { +- if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && +- id->idVendor != le16_to_cpu(dev->descriptor.idVendor)) +- continue; +- +- if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && +- id->idProduct != le16_to_cpu(dev->descriptor.idProduct)) +- continue; +- +- /* No need to test id->bcdDevice_lo != 0, since 0 is never +- greater than any unsigned number. */ +- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && +- (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice))) +- continue; +- +- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && +- (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice))) +- continue; +- +- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && +- (id->bDeviceClass != dev->descriptor.bDeviceClass)) +- continue; +- +- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && +- (id->bDeviceSubClass != dev->descriptor.bDeviceSubClass)) +- continue; +- +- if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && +- (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) +- continue; ++#ifdef DEBUG ++ dev_dbg(&dev->dev, "device V:%04x P:%04x DC:%04x SC:%04x PR:%04x \n", ++ dev->descriptor.idVendor, ++ dev->descriptor.idProduct, ++ dev->descriptor.bDeviceClass, ++ dev->descriptor.bDeviceSubClass, ++ dev->descriptor.bDeviceProtocol); ++#endif + + return 1; ++ /* NOTE: can't use usb_match_id() since interface caches ++ * aren't set up yet. this is cut/paste from that code. ++ */ ++ for (id = whitelist_table; id->match_flags; id++) { ++#ifdef DEBUG ++ dev_dbg(&dev->dev, ++ "ID: V:%04x P:%04x DC:%04x SC:%04x PR:%04x \n", ++ id->idVendor, ++ id->idProduct, ++ id->bDeviceClass, ++ id->bDeviceSubClass, ++ id->bDeviceProtocol); ++#endif ++ ++ if ((id->match_flags & USB_DEVICE_ID_MATCH_VENDOR) && ++ id->idVendor != le16_to_cpu(dev->descriptor.idVendor)) ++ continue; ++ ++ if ((id->match_flags & USB_DEVICE_ID_MATCH_PRODUCT) && ++ id->idProduct != le16_to_cpu(dev->descriptor.idProduct)) ++ continue; ++ ++ /* No need to test id->bcdDevice_lo != 0, since 0 is never ++ greater than any unsigned number. */ ++ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_LO) && ++ (id->bcdDevice_lo > le16_to_cpu(dev->descriptor.bcdDevice))) ++ continue; ++ ++ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_HI) && ++ (id->bcdDevice_hi < le16_to_cpu(dev->descriptor.bcdDevice))) ++ continue; ++ ++ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_CLASS) && ++ (id->bDeviceClass != dev->descriptor.bDeviceClass)) ++ continue; ++ ++ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_SUBCLASS) && ++ (id->bDeviceSubClass != dev->descriptor.bDeviceSubClass)) ++ continue; ++ ++ if ((id->match_flags & USB_DEVICE_ID_MATCH_DEV_PROTOCOL) && ++ (id->bDeviceProtocol != dev->descriptor.bDeviceProtocol)) ++ continue; ++ ++ return 1; ++ } + } + + /* add other match criteria here ... */ + +- +- /* OTG MESSAGE: report errors here, customize to match your product */ +- dev_err(&dev->dev, "device v%04x p%04x is not supported\n", +- le16_to_cpu(dev->descriptor.idVendor), +- le16_to_cpu(dev->descriptor.idProduct)); + #ifdef CONFIG_USB_OTG_WHITELIST ++ report_errors(dev); + return 0; + #else +- return 1; ++ if (enable_whitelist) { ++ report_errors(dev); ++ return 0; ++ } else { ++ return 1; ++ } + #endif + } + +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -536,6 +536,34 @@ config USB_GADGET_SUPERSPEED + bool + depends on USB_GADGET_DUALSPEED + ++config USB_GADGET_SNPS_DWC_OTG ++ boolean "Synopsys Driver for DWC_otg Controller" ++ depends on USB && EXPERIMENTAL ++ select USB_OTG ++ select USB_GADGET_DUALSPEED ++ help ++ Selects the Synopsys Driver for the DWC_otg Controller. ++ ++config USB_DWC_OTG_LPM ++ boolean "Enable LPM support" ++ depends on USB && EXPERIMENTAL ++ help ++ Enables LPM support. ++ ++config USB_GADGET_SNPS_DWC_OTG ++ boolean "Synopsys Driver for DWC_otg Controller" ++ depends on USB && EXPERIMENTAL ++ select USB_OTG ++ select USB_GADGET_DUALSPEED ++ help ++ Selects the Synopsys Driver for the DWC_otg Controller. ++ ++config USB_DWC_OTG_LPM ++ boolean "Enable LPM support" ++ depends on USB && EXPERIMENTAL ++ help ++ Enables LPM support. ++ + # + # USB Gadget Drivers + # +--- a/drivers/usb/gadget/file_storage.c ++++ b/drivers/usb/gadget/file_storage.c +@@ -573,8 +573,37 @@ config_desc = { + .iConfiguration = FSG_STRING_CONFIG, + .bmAttributes = USB_CONFIG_ATT_ONE | USB_CONFIG_ATT_SELFPOWER, + .bMaxPower = CONFIG_USB_GADGET_VBUS_DRAW / 2, ++ //.bMaxPower = 0, //unused suggestion by DWC patch + }; + ++#ifdef CONFIG_USB_DWC_OTG_LPM ++#define USB_DEVICE_CAPABILITY_20_EXTENSION 0x02 ++#define USB_20_EXT_LPM 0x02 ++typedef struct usb_dev_cap_20_ext_desc { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ __u8 bDevCapabilityType; ++ __le32 bmAttributes; ++} __attribute__ ((__packed__)) usb_dev_cap_20_ext_desc_t; ++ ++static struct usb_bos_20_ext_desc { ++ struct usb_bos_descriptor bos_desc; ++ struct usb_dev_cap_20_ext_desc dev_cap_20_ext_desc; ++} __attribute__ ((__packed__)) bos_20_ext_desc = { ++ { ++ .bLength = sizeof(struct usb_bos_descriptor), ++ .bDescriptorType = USB_DT_BOS, ++ .wTotalLength = sizeof(struct usb_bos_20_ext_desc), ++ .bNumDeviceCaps = 1, ++ }, ++ { ++ .bLength = sizeof(struct usb_dev_cap_20_ext_desc), ++ .bDescriptorType = USB_DT_DEVICE_CAPABILITY, ++ .bDevCapabilityType = USB_DEVICE_CAPABILITY_20_EXTENSION, ++ .bmAttributes = USB_20_EXT_LPM, ++ }, ++}; ++#endif + + static struct usb_qualifier_descriptor + dev_qualifier = { +@@ -989,6 +1018,24 @@ get_config: + if (gadget_is_superspeed(fsg->gadget)) + value = populate_bos(fsg, req->buf); + break; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ case USB_DT_BOS: ++ /* When the PCD has LPM enabled set the LPM ++ * Feature bit to 1 when not enabled set the ++ * bit to 0. */ ++ if (usb_gadget_test_lpm_support(fsg->gadget)) { ++ VDBG(fsg, "LPM support enabled in DWC UDC PCD\n"); ++ bos_20_ext_desc.dev_cap_20_ext_desc.bmAttributes |= USB_20_EXT_LPM; ++ } else { ++ VDBG(fsg, "LPM support disabled in DWC UDC PCD\n"); ++ bos_20_ext_desc.dev_cap_20_ext_desc.bmAttributes &= ~USB_20_EXT_LPM; ++ } ++ DBG(fsg, "sending BOS descriptor to host\n"); ++ value = sizeof bos_20_ext_desc; ++ memcpy(req->buf, &bos_20_ext_desc, value); ++ break; ++#endif ++ + } + + break; +@@ -2650,6 +2697,9 @@ static int received_cbw(struct fsg_dev * + fsg_set_halt(fsg, fsg->bulk_out); + halt_bulk_in_endpoint(fsg); + } ++ fsg->bulk_in->ops->set_halt(fsg->bulk_in, 3); ++ fsg_set_halt(fsg, fsg->bulk_out); ++ fsg->bulk_out->ops->set_halt(fsg->bulk_out, 3); + return -EINVAL; + } + +@@ -3011,7 +3061,8 @@ static void handle_exception(struct fsg_ + * bulk endpoint, clear the halt now. (The SuperH UDC + * requires this.) */ + if (test_and_clear_bit(IGNORE_BULK_OUT, &fsg->atomic_bitflags)) +- usb_ep_clear_halt(fsg->bulk_in); ++ //usb_ep_clear_halt(fsg->bulk_in); //DWC patch: ++ fsg->bulk_in->ops->set_halt(fsg->bulk_in, 2); + + if (transport_is_bbb()) { + if (fsg->ep0_req_tag == exception_req_tag) +@@ -3085,6 +3136,9 @@ static int fsg_main_thread(void *fsg_) + * that expects a __user pointer and it will work okay. */ + set_fs(get_ds()); + ++ /* Setting this thread high priority */ ++ set_user_nice(current, -20); ++ + /* The main loop */ + while (fsg->state != FSG_STATE_TERMINATED) { + if (exception_in_progress(fsg) || signal_pending(current)) { +@@ -3232,6 +3286,13 @@ static int __init check_parameters(struc + gcnum = usb_gadget_controller_number(fsg->gadget); + if (gcnum >= 0) + mod_data.release = 0x0300 + gcnum; ++ else if (gadget_is_dwc_otg(fsg->gadget)) { ++ mod_data.release = __constant_cpu_to_le16 (0x0200); ++ mod_data.vendor = __constant_cpu_to_le16 (0x053f); ++ if (mod_data.product == DRIVER_PRODUCT_ID) { ++ mod_data.product = __constant_cpu_to_le16 (0x0000); ++ } ++ } + else { + WARNING(fsg, "controller '%s' not recognized\n", + fsg->gadget->name); +@@ -3493,6 +3554,13 @@ static int __init fsg_bind(struct usb_ga + + rc = -ENOMEM; + ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ /* When LPM is enabled, Inform the host that the remote wake ++ * up capability is supported. */ ++ if (usb_gadget_test_lpm_support(fsg->gadget)) ++ config_desc.bmAttributes |= USB_CONFIG_ATT_WAKEUP; ++#endif ++ + /* Allocate the request and buffer for endpoint 0 */ + fsg->ep0req = req = usb_ep_alloc_request(fsg->ep0, GFP_KERNEL); + if (!req) +--- a/drivers/usb/host/Kconfig ++++ b/drivers/usb/host/Kconfig +@@ -571,6 +571,19 @@ config USB_HWA_HCD + To compile this driver a module, choose M here: the module + will be called "hwa-hc". + ++config USB_DWCOTG ++ tristate "Synopsis DWC host support" ++ depends on USB ++ help ++ The Synopsis DWC controller is a dual-role ++ host/peripheral/OTG ("On The Go") USB controllers. ++ ++ Enable this option to support this IP in host controller mode. ++ If unsure, say N. ++ ++ To compile this driver as a module, choose M here: the ++ modules built will be called dwc_otg and dwc_common_port. ++ + config USB_IMX21_HCD + tristate "i.MX21 HCD support" + depends on USB && ARM && ARCH_MXC +--- a/drivers/usb/host/Makefile ++++ b/drivers/usb/host/Makefile +@@ -33,6 +33,8 @@ obj-$(CONFIG_USB_U132_HCD) += u132-hcd.o + obj-$(CONFIG_USB_R8A66597_HCD) += r8a66597-hcd.o + obj-$(CONFIG_USB_ISP1760_HCD) += isp1760.o + obj-$(CONFIG_USB_HWA_HCD) += hwa-hc.o ++ ++obj-$(CONFIG_USB_DWCOTG) += dwc_otg/ dwc_common_port/ + obj-$(CONFIG_USB_IMX21_HCD) += imx21-hcd.o + obj-$(CONFIG_USB_FSL_MPH_DR_OF) += fsl-mph-dr-of.o + obj-$(CONFIG_USB_OCTEON2_COMMON) += octeon2-common.o +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/Makefile +@@ -0,0 +1,44 @@ ++# ++# Makefile for DWC_common library ++# ++ ++ifneq ($(KERNELRELEASE),) ++ ++#CPPFLAGS += -DDEBUG_MEMORY ++ ++CPPFLAGS += -DDEBUG ++CPPFLAGS += -DDWC_LINUX ++ ++obj-$(CONFIG_USB_DWCOTG) += dwc_common_port_lib.o ++dwc_common_port_lib-objs := dwc_cc.o dwc_modpow.o dwc_dh.o \ ++ dwc_crypto.o dwc_notifier.o \ ++ dwc_common_linux.o dwc_mem.o ++ ++kernrelwd := $(subst ., ,$(KERNELRELEASE)) ++kernrel3 := $(word 1,$(kernrelwd)).$(word 2,$(kernrelwd)).$(word 3,$(kernrelwd)) ++ ++ifneq ($(kernrel3),2.6.20) ++# grayg - I only know that we use EXTRA_CFLAGS in 2.6.31 actually ++EXTRA_CFLAGS += $(CPPFLAGS) ++endif ++ ++else ++ ++ifeq ($(DOXYGEN),) ++DOXYGEN := $(DOXYGEN) ++endif ++ ++default: ++ $(MAKE) -C$(KDIR) M=$(PWD) modules ++ ++docs: $(wildcard *.[hc]) doc/doxygen.cfg ++ $(DOXYGEN) doc/doxygen.cfg ++ ++tags: $(wildcard *.[hc]) ++ $(CTAGS) -e $(wildcard *.[hc]) $(wildcard linux/*.[hc]) $(wildcard $(KDIR)/include/linux/usb*.h) ++ ++endif ++ ++clean: ++ rm -rf *.o *.ko .*cmd *.mod.c .tmp_versions Module.symvers ++ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/Makefile.linux +@@ -0,0 +1,36 @@ ++# ++# Makefile for DWC_common library ++# ++ifneq ($(KERNELRELEASE),) ++ ++#CPPFLAGS += -DDEBUG_MEMORY ++ ++#CPPFLAGS += -DDEBUG ++CPPFLAGS += -DDWC_LINUX ++ ++obj-m := dwc_common_port_lib.o ++dwc_common_port_lib-objs := dwc_cc.o dwc_modpow.o dwc_dh.o \ ++ dwc_crypto.o dwc_notifier.o \ ++ dwc_common_linux.o dwc_mem.o ++ ++else ++ ++ ++ifeq ($(DOXYGEN),) ++DOXYGEN := $(DOXYGEN) ++endif ++ ++default: ++ $(MAKE) -C$(KDIR) M=$(PWD) modules ++ ++docs: $(wildcard *.[hc]) doc/doxygen.cfg ++ $(DOXYGEN) doc/doxygen.cfg ++ ++tags: $(wildcard *.[hc]) ++ $(CTAGS) -e $(wildcard *.[hc]) $(wildcard linux/*.[hc]) $(wildcard $(KDIR)/include/linux/usb*.h) ++ ++endif ++ ++clean: ++ rm -rf *.o *.ko .*cmd *.mod.c .tmp_versions Module.symvers ++ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_cc.c +@@ -0,0 +1,506 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port/dwc_cc.c $ ++ * $Revision: #1 $ ++ * $Date: 2008/12/21 $ ++ * $Change: 1156609 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++#include "dwc_cc.h" ++ ++typedef struct dwc_cc ++{ ++ uint32_t uid; ++ uint8_t chid[16]; ++ uint8_t cdid[16]; ++ uint8_t ck[16]; ++ uint8_t *name; ++ uint8_t length; ++ DWC_CIRCLEQ_ENTRY(dwc_cc) list_entry; ++} dwc_cc_t; ++ ++DWC_CIRCLEQ_HEAD(context_list, dwc_cc); ++ ++/** The main structure for CC management. */ ++struct dwc_cc_if ++{ ++ dwc_mutex_t *mutex; ++ char *filename; ++ ++ unsigned is_host:1; ++ ++ dwc_notifier_t *notifier; ++ ++ struct context_list list; ++}; ++ ++#ifdef DEBUG ++static inline void dump_bytes(char *name, uint8_t *bytes, int len) ++{ ++ int i; ++ DWC_PRINTF("%s: ", name); ++ for (i=0; i<len; i++) { ++ DWC_PRINTF("%02x ", bytes[i]); ++ } ++ DWC_PRINTF("\n"); ++} ++#else ++#define dump_bytes(x...) ++#endif ++ ++static dwc_cc_t *alloc_cc(uint8_t *name, uint32_t length) ++{ ++ dwc_cc_t *cc = DWC_ALLOC(sizeof(dwc_cc_t)); ++ if (!cc) { ++ return NULL; ++ } ++ DWC_MEMSET(cc, 0, sizeof(dwc_cc_t)); ++ ++ if (name) { ++ cc->length = length; ++ cc->name = DWC_ALLOC(length); ++ DWC_MEMCPY(cc->name, name, length); ++ } ++ ++ return cc; ++} ++ ++static void free_cc(dwc_cc_t *cc) ++{ ++ if (cc->name) { ++ DWC_FREE(cc->name); ++ } ++ DWC_FREE(cc); ++} ++ ++static uint32_t next_uid(dwc_cc_if_t *cc_if) ++{ ++ uint32_t uid = 0; ++ dwc_cc_t *cc; ++ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) { ++ if (cc->uid > uid) { ++ uid = cc->uid; ++ } ++ } ++ ++ if (uid == 0) { ++ uid = 255; ++ } ++ ++ return uid + 1; ++} ++ ++static dwc_cc_t *cc_find(dwc_cc_if_t *cc_if, uint32_t uid) ++{ ++ dwc_cc_t *cc; ++ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) { ++ if (cc->uid == uid) { ++ return cc; ++ } ++ } ++ return NULL; ++} ++ ++static unsigned int cc_data_size(dwc_cc_if_t *cc_if) ++{ ++ unsigned int size = 0; ++ dwc_cc_t *cc; ++ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) { ++ size += (48 + 1); ++ if (cc->name) { ++ size += cc->length; ++ } ++ } ++ return size; ++} ++ ++static uint32_t cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid) ++{ ++ uint32_t uid = 0; ++ dwc_cc_t *cc; ++ ++ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) { ++ if (DWC_MEMCMP(cc->chid, chid, 16) == 0) { ++ uid = cc->uid; ++ break; ++ } ++ } ++ return uid; ++} ++static uint32_t cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid) ++{ ++ uint32_t uid = 0; ++ dwc_cc_t *cc; ++ ++ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) { ++ if (DWC_MEMCMP(cc->cdid, cdid, 16) == 0) { ++ uid = cc->uid; ++ break; ++ } ++ } ++ return uid; ++} ++ ++/* Internal cc_add */ ++static int32_t cc_add(dwc_cc_if_t *cc_if, uint8_t *chid, uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length) ++{ ++ dwc_cc_t *cc; ++ uint32_t uid; ++ ++ if (cc_if->is_host) { ++ uid = cc_match_cdid(cc_if, cdid); ++ } ++ else { ++ uid = cc_match_chid(cc_if, chid); ++ } ++ ++ if (uid) { ++ DWC_DEBUG("Replacing previous connection context id=%d name=%p name_len=%d", uid, name, length); ++ cc = cc_find(cc_if, uid); ++ } ++ else { ++ cc = alloc_cc(name, length); ++ cc->uid = next_uid(cc_if); ++ DWC_CIRCLEQ_INSERT_TAIL(&cc_if->list, cc, list_entry); ++ } ++ ++ DWC_MEMCPY(&(cc->chid[0]), chid, 16); ++ DWC_MEMCPY(&(cc->cdid[0]), cdid, 16); ++ DWC_MEMCPY(&(cc->ck[0]), ck, 16); ++ ++ DWC_DEBUG("Added connection context id=%d name=%p name_len=%d", cc->uid, name, length); ++ dump_bytes("CHID", cc->chid, 16); ++ dump_bytes("CDID", cc->cdid, 16); ++ dump_bytes("CK", cc->ck, 16); ++ return cc->uid; ++} ++ ++/* Internal cc_clear */ ++static void cc_clear(dwc_cc_if_t *cc_if) ++{ ++ while (!DWC_CIRCLEQ_EMPTY(&cc_if->list)) { ++ dwc_cc_t *cc = DWC_CIRCLEQ_FIRST(&cc_if->list); ++ DWC_CIRCLEQ_REMOVE_INIT(&cc_if->list, cc, list_entry); ++ free_cc(cc); ++ } ++} ++ ++dwc_cc_if_t *dwc_cc_if_alloc(dwc_notifier_t *notifier, unsigned is_host) ++{ ++ dwc_cc_if_t *cc_if = NULL; ++ ++ /* Allocate a common_cc_if structure */ ++ cc_if = DWC_ALLOC(sizeof(dwc_cc_if_t)); ++ ++ if(!cc_if) ++ return NULL; ++ ++#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)) ++ DWC_MUTEX_ALLOC_LINUX_DEBUG(cc_if->mutex); ++#else ++ cc_if->mutex = DWC_MUTEX_ALLOC(); ++#endif ++ DWC_CIRCLEQ_INIT(&cc_if->list); ++ cc_if->is_host = is_host; ++ cc_if->notifier = notifier; ++ return cc_if; ++} ++ ++void dwc_cc_if_free(dwc_cc_if_t *cc_if) ++{ ++ DWC_MUTEX_FREE(cc_if->mutex); ++ cc_clear(cc_if); ++ DWC_FREE(cc_if); ++} ++ ++static void cc_changed(dwc_cc_if_t *cc_if) ++{ ++ if (cc_if->notifier) { ++ dwc_notify(cc_if->notifier, DWC_CC_LIST_CHANGED_NOTIFICATION, cc_if); ++ } ++} ++ ++void dwc_cc_clear(dwc_cc_if_t *cc_if) ++{ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc_clear(cc_if); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ cc_changed(cc_if); ++} ++ ++int32_t dwc_cc_add(dwc_cc_if_t *cc_if, uint8_t *chid, uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length) ++{ ++ uint32_t uid; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ uid = cc_add(cc_if, chid, cdid, ck, name, length); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ cc_changed(cc_if); ++ ++ return uid; ++} ++ ++void dwc_cc_change(dwc_cc_if_t *cc_if, int32_t id, ++ uint8_t *chid, uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length) ++{ ++ dwc_cc_t* cc; ++ ++ DWC_DEBUG("Change connection context %d", id); ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc = cc_find(cc_if, id); ++ if (!cc) { ++ DWC_ERROR("Uid %d not found in cc list", id); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ return; ++ } ++ ++ if (chid) { ++ DWC_MEMCPY(&(cc->chid[0]), chid, 16); ++ } ++ if (cdid) { ++ DWC_MEMCPY(&(cc->cdid[0]), cdid, 16); ++ } ++ if (ck) { ++ DWC_MEMCPY(&(cc->ck[0]), ck, 16); ++ } ++ ++ if (name) { ++ if (cc->name) { ++ DWC_FREE(cc->name); ++ } ++ cc->name = DWC_ALLOC(length); ++ cc->length = length; ++ DWC_MEMCPY(cc->name, name, length); ++ } ++ ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ cc_changed(cc_if); ++ ++ DWC_DEBUG("Changed connection context id=%d\n", id); ++ dump_bytes("New CHID", cc->chid, 16); ++ dump_bytes("New CDID", cc->cdid, 16); ++ dump_bytes("New CK", cc->ck, 16); ++} ++ ++void dwc_cc_remove(dwc_cc_if_t *cc_if, int32_t id) ++{ ++ dwc_cc_t *cc; ++ ++ DWC_DEBUG("Removing connection context %d", id); ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc = cc_find(cc_if, id); ++ if (!cc) { ++ DWC_ERROR("Uid %d not found in cc list", id); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ return; ++ } ++ ++ DWC_CIRCLEQ_REMOVE_INIT(&cc_if->list, cc, list_entry); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ free_cc(cc); ++ ++ cc_changed(cc_if); ++} ++ ++uint8_t *dwc_cc_data_for_save(dwc_cc_if_t *cc_if, unsigned int *length) ++{ ++ uint8_t *buf, *x; ++ uint8_t zero = 0; ++ dwc_cc_t *cc; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ *length = cc_data_size(cc_if); ++ if (!(*length)) { ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ return NULL; ++ } ++ ++ DWC_DEBUG("Creating data for saving (length=%d)", *length); ++ ++ buf = DWC_ALLOC(*length); ++ if (!buf) { ++ *length = 0; ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ return NULL; ++ } ++ ++ x = buf; ++ DWC_CIRCLEQ_FOREACH(cc, &cc_if->list, list_entry) { ++ DWC_MEMCPY(x, cc->chid, 16); ++ x += 16; ++ DWC_MEMCPY(x, cc->cdid, 16); ++ x += 16; ++ DWC_MEMCPY(x, cc->ck, 16); ++ x += 16; ++ if (cc->name) { ++ DWC_MEMCPY(x, &cc->length, 1); ++ x += 1; ++ DWC_MEMCPY(x, cc->name, cc->length); ++ x += cc->length; ++ } ++ else { ++ DWC_MEMCPY(x, &zero, 1); ++ x += 1; ++ } ++ } ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ return buf; ++} ++ ++void dwc_cc_restore_from_data(dwc_cc_if_t *cc_if, uint8_t *data, uint32_t length) ++{ ++ uint8_t name_length; ++ uint8_t *name; ++ uint8_t *chid; ++ uint8_t *cdid; ++ uint8_t *ck; ++ uint32_t i = 0; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc_clear(cc_if); ++ ++ while (i < length) { ++ chid = &data[i]; ++ i += 16; ++ cdid = &data[i]; ++ i += 16; ++ ck = &data[i]; ++ i += 16; ++ ++ name_length = data[i]; ++ i ++; ++ ++ if (name_length) { ++ name = &data[i]; ++ i += name_length; ++ } ++ else { ++ name = NULL; ++ } ++ ++ /* check to see if we haven't overflown the buffer */ ++ if (i > length) { ++ DWC_ERROR("Data format error while attempting to load CCs " ++ "(nlen=%d, iter=%d, buflen=%d).", name_length, i, length); ++ break; ++ } ++ ++ cc_add(cc_if, chid, cdid, ck, name, name_length); ++ } ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ cc_changed(cc_if); ++} ++ ++uint32_t dwc_cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid) ++{ ++ uint32_t uid = 0; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ uid = cc_match_chid(cc_if, chid); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ return uid; ++} ++uint32_t dwc_cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid) ++{ ++ uint32_t uid = 0; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ uid = cc_match_cdid(cc_if, cdid); ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ return uid; ++} ++ ++uint8_t *dwc_cc_ck(dwc_cc_if_t *cc_if, int32_t id) ++{ ++ uint8_t *ck = NULL; ++ dwc_cc_t *cc; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc = cc_find(cc_if, id); ++ if (cc) { ++ ck = cc->ck; ++ } ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ return ck; ++ ++} ++ ++uint8_t *dwc_cc_chid(dwc_cc_if_t *cc_if, int32_t id) ++{ ++ uint8_t *retval = NULL; ++ dwc_cc_t *cc; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc = cc_find(cc_if, id); ++ if (cc) { ++ retval = cc->chid; ++ } ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ return retval; ++} ++ ++uint8_t *dwc_cc_cdid(dwc_cc_if_t *cc_if, int32_t id) ++{ ++ uint8_t *retval = NULL; ++ dwc_cc_t *cc; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ cc = cc_find(cc_if, id); ++ if (cc) { ++ retval = cc->cdid; ++ } ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ return retval; ++} ++ ++uint8_t *dwc_cc_name(dwc_cc_if_t *cc_if, int32_t id, uint8_t *length) ++{ ++ uint8_t *retval = NULL; ++ dwc_cc_t *cc; ++ ++ DWC_MUTEX_LOCK(cc_if->mutex); ++ *length = 0; ++ cc = cc_find(cc_if, id); ++ if (cc) { ++ *length = cc->length; ++ retval = cc->name; ++ } ++ DWC_MUTEX_UNLOCK(cc_if->mutex); ++ ++ return retval; ++} +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_cc.h +@@ -0,0 +1,209 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port/dwc_cc.h $ ++ * $Revision: #1 $ ++ * $Date: 2008/12/21 $ ++ * $Change: 1156609 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++#ifndef _DWC_CC_H_ ++#define _DWC_CC_H_ ++ ++/** @file ++ * ++ * This file defines the Context Context library. ++ * ++ * The main data structure is dwc_cc_if_t which is returned by either the ++ * dwc_cc_if_alloc function or returned by the module to the user via a provided ++ * function. The data structure is opaque and should only be manipulated via the ++ * functions provied in this API. ++ * ++ * It manages a list of connection contexts and operations can be performed to ++ * add, remove, query, search, and change, those contexts. Additionally, ++ * a dwc_notifier_t object can be requested from the manager so that ++ * the user can be notified whenever the context list has changed. ++ */ ++ ++#include "dwc_os.h" ++#include "dwc_list.h" ++#include "dwc_notifier.h" ++ ++ ++/* Notifications */ ++#define DWC_CC_LIST_CHANGED_NOTIFICATION "DWC_CC_LIST_CHANGED_NOTIFICATION" ++ ++struct dwc_cc_if; ++typedef struct dwc_cc_if dwc_cc_if_t; ++ ++ ++/** @name Connection Context Operations */ ++/** @{ */ ++ ++/** This function allocates memory for a dwc_cc_if_t structure, initializes ++ * fields to default values, and returns a pointer to the structure or NULL on ++ * error. */ ++extern dwc_cc_if_t *dwc_cc_if_alloc(dwc_notifier_t *notifier, unsigned is_host); ++ ++/** Frees the memory for the specified CC structure allocated from ++ * dwc_cc_if_alloc(). */ ++extern void dwc_cc_if_free(dwc_cc_if_t *cc_if); ++ ++/** Removes all contexts from the connection context list */ ++extern void dwc_cc_clear(dwc_cc_if_t *cc_if); ++ ++/** Adds a connection context (CHID, CK, CDID, Name) to the connection context list. ++ * If a CHID already exists, the CK and name are overwritten. Statistics are ++ * not overwritten. ++ * ++ * @param cc_if The cc_if structure. ++ * @param chid A pointer to the 16-byte CHID. This value will be copied. ++ * @param ck A pointer to the 16-byte CK. This value will be copied. ++ * @param cdid A pointer to the 16-byte CDID. This value will be copied. ++ * @param name An optional host friendly name as defined in the association model ++ * spec. Must be a UTF16-LE unicode string. Can be NULL to indicated no name. ++ * @param length The length othe unicode string. ++ * @return A unique identifier used to refer to this context that is valid for ++ * as long as this context is still in the list. */ ++extern int32_t dwc_cc_add(dwc_cc_if_t *cc_if, uint8_t *chid, uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length); ++ ++/** Changes the CHID, CK, CDID, or Name values of a connection context in the ++ * list, preserving any accumulated statistics. This would typically be called ++ * if the host decideds to change the context with a SET_CONNECTION request. ++ * ++ * @param cc_if The cc_if structure. ++ * @param id The identifier of the connection context. ++ * @param chid A pointer to the 16-byte CHID. This value will be copied. NULL ++ * indicates no change. ++ * @param cdid A pointer to the 16-byte CDID. This value will be copied. NULL ++ * indicates no change. ++ * @param ck A pointer to the 16-byte CK. This value will be copied. NULL ++ * indicates no change. ++ * @param name Host friendly name UTF16-LE. NULL indicates no change. ++ * @param length Length of name. */ ++extern void dwc_cc_change(dwc_cc_if_t *cc_if, int32_t id, uint8_t *chid, uint8_t *cdid, uint8_t *ck, uint8_t *name, uint8_t length); ++ ++/** Remove the specified connection context. ++ * @param cc_if The cc_if structure. ++ * @param id The identifier of the connection context to remove. */ ++extern void dwc_cc_remove(dwc_cc_if_t *cc_if, int32_t id); ++ ++/** Get a binary block of data for the connection context list and attributes. ++ * This data can be used by the OS specific driver to save the connection ++ * context list into non-volatile memory. ++ * ++ * @param cc_if The cc_if structure. ++ * @param length Return the length of the data buffer. ++ * @return A pointer to the data buffer. The memory for this buffer should be freed with DWC_FREE() after use. */ ++extern uint8_t *dwc_cc_data_for_save(dwc_cc_if_t *cc_if, unsigned int *length); ++ ++/** Restore the connection context list from the binary data that was previously ++ * returned from a call to dwc_cc_data_for_save. This can be used by the OS specific ++ * driver to load a connection context list from non-volatile memory. ++ * ++ * @param cc_if The cc_if structure. ++ * @param data The data bytes as returned from dwc_cc_data_for_save. ++ * @param length The length of the data. */ ++extern void dwc_cc_restore_from_data(dwc_cc_if_t *cc_if, uint8_t *data, unsigned int length); ++ ++/** Find the connection context from the specified CHID. ++ * ++ * @param cc_if The cc_if structure. ++ * @param chid A pointer to the CHID data. ++ * @return A non-zero identifier of the connection context if the CHID matches. ++ * Otherwise returns 0. */ ++extern uint32_t dwc_cc_match_chid(dwc_cc_if_t *cc_if, uint8_t *chid); ++ ++/** Find the connection context from the specified CDID. ++ * ++ * @param cc_if The cc_if structure. ++ * @param cdid A pointer to the CDID data. ++ * @return A non-zero identifier of the connection context if the CHID matches. ++ * Otherwise returns 0. */ ++extern uint32_t dwc_cc_match_cdid(dwc_cc_if_t *cc_if, uint8_t *cdid); ++ ++/** Retrieve the CK from the specified connection context. ++ * ++ * @param cc_if The cc_if structure. ++ * @param id The identifier of the connection context. ++ * @return A pointer to the CK data. The memory does not need to be freed. */ ++extern uint8_t *dwc_cc_ck(dwc_cc_if_t *cc_if, int32_t id); ++ ++/** Retrieve the CHID from the specified connection context. ++ * ++ * @param cc_if The cc_if structure. ++ * @param id The identifier of the connection context. ++ * @return A pointer to the CHID data. The memory does not need to be freed. */ ++extern uint8_t *dwc_cc_chid(dwc_cc_if_t *cc_if, int32_t id); ++ ++/** Retrieve the CDID from the specified connection context. ++ * ++ * @param cc_if The cc_if structure. ++ * @param id The identifier of the connection context. ++ * @return A pointer to the CDID data. The memory does not need to be freed. */ ++extern uint8_t *dwc_cc_cdid(dwc_cc_if_t *cc_if, int32_t id); ++ ++extern uint8_t *dwc_cc_name(dwc_cc_if_t *cc_if, int32_t id, uint8_t *length); ++ ++/** Checks a buffer for non-zero. ++ * @param id A pointer to a 16 byte buffer. ++ * @return true if the 16 byte value is non-zero. */ ++static inline unsigned dwc_assoc_is_not_zero_id(uint8_t *id) { ++ int i; ++ for (i=0; i<16; i++) { ++ if (id[i]) return 1; ++ } ++ return 0; ++} ++ ++/** Checks a buffer for zero. ++ * @param id A pointer to a 16 byte buffer. ++ * @return true if the 16 byte value is zero. */ ++static inline unsigned dwc_assoc_is_zero_id(uint8_t *id) { ++ return !dwc_assoc_is_not_zero_id(id); ++} ++ ++/** Prints an ASCII representation for the 16-byte chid, cdid, or ck, into ++ * buffer. */ ++static inline int dwc_print_id_string(char *buffer, uint8_t *id) { ++ char *ptr = buffer; ++ int i; ++ for (i=0; i<16; i++) { ++ ptr += DWC_SPRINTF(ptr, "%02x", id[i]); ++ if (i < 15) { ++ ptr += DWC_SPRINTF(ptr, " "); ++ } ++ } ++ return ptr - buffer; ++} ++ ++/** @} */ ++ ++#endif /* _DWC_CC_H_ */ ++ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_common_linux.c +@@ -0,0 +1,1247 @@ ++#include "dwc_cc.h" ++#include "dwc_modpow.h" ++#include "dwc_dh.h" ++#include "dwc_crypto.h" ++#include "dwc_notifier.h" ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/kthread.h> ++ ++MODULE_DESCRIPTION("DWC Common Library - Portable version"); ++MODULE_AUTHOR("Synopsys Inc."); ++MODULE_LICENSE ("GPL"); ++ ++static int dwc_common_port_init_module(void) ++{ ++ printk( KERN_DEBUG "Module dwc_common_port init\n" ); ++#ifdef DEBUG_MEMORY ++ dwc_memory_debug_start(); ++#endif ++ dwc_alloc_notification_manager(); ++ return 0; ++} ++ ++static void dwc_common_port_exit_module(void) ++{ ++ printk( KERN_DEBUG "Module dwc_common_port exit\n" ); ++ dwc_free_notification_manager(); ++#ifdef DEBUG_MEMORY ++ dwc_memory_debug_stop(); ++#endif ++} ++ ++module_init(dwc_common_port_init_module); ++module_exit(dwc_common_port_exit_module); ++ ++/* CC */ ++EXPORT_SYMBOL(dwc_cc_if_alloc); ++EXPORT_SYMBOL(dwc_cc_if_free); ++EXPORT_SYMBOL(dwc_cc_clear); ++EXPORT_SYMBOL(dwc_cc_add); ++EXPORT_SYMBOL(dwc_cc_remove); ++EXPORT_SYMBOL(dwc_cc_change); ++EXPORT_SYMBOL(dwc_cc_data_for_save); ++EXPORT_SYMBOL(dwc_cc_restore_from_data); ++EXPORT_SYMBOL(dwc_cc_match_chid); ++EXPORT_SYMBOL(dwc_cc_match_cdid); ++EXPORT_SYMBOL(dwc_cc_ck); ++EXPORT_SYMBOL(dwc_cc_chid); ++EXPORT_SYMBOL(dwc_cc_cdid); ++EXPORT_SYMBOL(dwc_cc_name); ++ ++#ifndef CONFIG_MACH_IPMATE ++/* Modpow */ ++EXPORT_SYMBOL(dwc_modpow); ++/* DH */ ++EXPORT_SYMBOL(dwc_dh_modpow); ++EXPORT_SYMBOL(dwc_dh_derive_keys); ++EXPORT_SYMBOL(dwc_dh_pk); ++#endif /* CONFIG_MACH_IPMATE */ ++/* Crypto */ ++EXPORT_SYMBOL(dwc_wusb_aes_encrypt); ++EXPORT_SYMBOL(dwc_wusb_cmf); ++EXPORT_SYMBOL(dwc_wusb_prf); ++EXPORT_SYMBOL(dwc_wusb_fill_ccm_nonce); ++EXPORT_SYMBOL(dwc_wusb_gen_nonce); ++EXPORT_SYMBOL(dwc_wusb_gen_key); ++EXPORT_SYMBOL(dwc_wusb_gen_mic); ++ ++ ++/* Notification */ ++EXPORT_SYMBOL(dwc_alloc_notification_manager); ++EXPORT_SYMBOL(dwc_free_notification_manager); ++EXPORT_SYMBOL(dwc_register_notifier); ++EXPORT_SYMBOL(dwc_unregister_notifier); ++EXPORT_SYMBOL(dwc_add_observer); ++EXPORT_SYMBOL(dwc_remove_observer); ++EXPORT_SYMBOL(dwc_notify); ++ ++/* Memory Debugging Routines */ ++#ifdef DEBUG_MEMORY ++EXPORT_SYMBOL(dwc_alloc_debug); ++EXPORT_SYMBOL(dwc_alloc_atomic_debug); ++EXPORT_SYMBOL(dwc_free_debug); ++EXPORT_SYMBOL(dwc_dma_alloc_debug); ++EXPORT_SYMBOL(dwc_dma_alloc_atomic_debug); ++EXPORT_SYMBOL(dwc_dma_free_debug); ++#endif ++ ++/* OS-Level Implementations */ ++ ++/* This is the Linux kernel implementation of the DWC platform library. */ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/ctype.h> ++#include <linux/crypto.h> ++#include <linux/delay.h> ++#include <linux/device.h> ++#include <linux/dma-mapping.h> ++#include <linux/cdev.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++#include <linux/jiffies.h> ++#include <linux/list.h> ++#include <linux/pci.h> ++#include <linux/slab.h> ++#include <linux/stat.h> ++#include <linux/string.h> ++#include <linux/timer.h> ++#include <linux/version.h> ++#include <linux/usb.h> ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++#include <linux/usb_gadget.h> ++#else ++#include <linux/usb/gadget.h> ++#endif ++#include <linux/random.h> ++#include <asm/io.h> ++#include <asm/page.h> ++#include <asm/uaccess.h> ++#include <asm/unaligned.h> ++#include <asm/page.h> ++#include <linux/scatterlist.h> ++ ++/* MISC */ ++ ++void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size) ++{ ++ return memset(dest, byte, size); ++} ++EXPORT_SYMBOL(DWC_MEMSET); ++ ++void *DWC_MEMCPY(void *dest, void const *src, uint32_t size) ++{ ++ return memcpy(dest, src, size); ++} ++EXPORT_SYMBOL(DWC_MEMCPY); ++ ++void *DWC_MEMMOVE(void *dest, void *src, uint32_t size) ++{ ++ return memmove(dest, src, size); ++} ++EXPORT_SYMBOL(DWC_MEMMOVE); ++ ++int DWC_MEMCMP(void *m1, void *m2, uint32_t size) ++{ ++ return memcmp(m1, m2, size); ++} ++EXPORT_SYMBOL(DWC_MEMCMP); ++ ++int DWC_STRNCMP(void *s1, void *s2, uint32_t size) ++{ ++ return strncmp(s1, s2, size); ++} ++EXPORT_SYMBOL(DWC_STRNCMP); ++ ++int DWC_STRCMP(void *s1, void *s2) ++{ ++ return strcmp(s1, s2); ++} ++EXPORT_SYMBOL(DWC_STRCMP); ++ ++int DWC_STRLEN(char const *str) ++{ ++ return strlen(str); ++} ++EXPORT_SYMBOL(DWC_STRLEN); ++ ++char *DWC_STRCPY(char *to, const char *from) ++{ ++ return strcpy(to, from); ++} ++EXPORT_SYMBOL(DWC_STRCPY); ++ ++char *DWC_STRDUP(char const *str) ++{ ++ int len = DWC_STRLEN(str) + 1; ++ char *new = DWC_ALLOC_ATOMIC(len); ++ if (!new) { ++ return NULL; ++ } ++ DWC_MEMCPY(new, str, len); ++ return new; ++} ++EXPORT_SYMBOL(DWC_STRDUP); ++ ++int DWC_ATOI(char *str, int32_t *value) ++{ ++ char *end = NULL; ++ *value = simple_strtol(str, &end, 0); ++ if (*end == '\0') { ++ return 0; ++ } ++ return -1; ++} ++EXPORT_SYMBOL(DWC_ATOI); ++ ++int DWC_ATOUI(char *str, uint32_t *value) ++{ ++ char *end = NULL; ++ *value = simple_strtoul(str, &end, 0); ++ if (*end == '\0') { ++ return 0; ++ } ++ return -1; ++} ++EXPORT_SYMBOL(DWC_ATOUI); ++ ++ ++/* From usbstring.c */ ++int DWC_UTF8_TO_UTF16LE(uint8_t const *s, uint16_t *cp, unsigned len) ++{ ++ int count = 0; ++ u8 c; ++ u16 uchar; ++ ++ /* this insists on correct encodings, though not minimal ones. ++ * BUT it currently rejects legit 4-byte UTF-8 code points, ++ * which need surrogate pairs. (Unicode 3.1 can use them.) ++ */ ++ while (len != 0 && (c = (u8) *s++) != 0) { ++ if (unlikely(c & 0x80)) { ++ // 2-byte sequence: ++ // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx ++ if ((c & 0xe0) == 0xc0) { ++ uchar = (c & 0x1f) << 6; ++ ++ c = (u8) *s++; ++ if ((c & 0xc0) != 0xc0) ++ goto fail; ++ c &= 0x3f; ++ uchar |= c; ++ ++ // 3-byte sequence (most CJKV characters): ++ // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx ++ } else if ((c & 0xf0) == 0xe0) { ++ uchar = (c & 0x0f) << 12; ++ ++ c = (u8) *s++; ++ if ((c & 0xc0) != 0xc0) ++ goto fail; ++ c &= 0x3f; ++ uchar |= c << 6; ++ ++ c = (u8) *s++; ++ if ((c & 0xc0) != 0xc0) ++ goto fail; ++ c &= 0x3f; ++ uchar |= c; ++ ++ /* no bogus surrogates */ ++ if (0xd800 <= uchar && uchar <= 0xdfff) ++ goto fail; ++ ++ // 4-byte sequence (surrogate pairs, currently rare): ++ // 11101110wwwwzzzzyy + 110111yyyyxxxxxx ++ // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx ++ // (uuuuu = wwww + 1) ++ // FIXME accept the surrogate code points (only) ++ ++ } else ++ goto fail; ++ } else ++ uchar = c; ++ put_unaligned (cpu_to_le16 (uchar), cp++); ++ count++; ++ len--; ++ } ++ return count; ++fail: ++ return -1; ++} ++EXPORT_SYMBOL(DWC_UTF8_TO_UTF16LE); ++ ++/* dwc_debug.h */ ++ ++dwc_bool_t DWC_IN_IRQ(void) ++{ ++ return in_irq(); ++} ++EXPORT_SYMBOL(DWC_IN_IRQ); ++ ++int DWC_IN_BH(void) ++{ ++ return in_softirq(); ++} ++EXPORT_SYMBOL(DWC_IN_BH); ++ ++void DWC_VPRINTF(char *format, va_list args) ++{ ++ vprintk(format, args); ++} ++EXPORT_SYMBOL(DWC_VPRINTF); ++ ++int DWC_VSNPRINTF(char *str, int size, char *format, va_list args) ++{ ++ return vsnprintf(str, size, format, args); ++} ++ ++void DWC_PRINTF(char *format, ...) ++{ ++ va_list args; ++ va_start(args, format); ++ DWC_VPRINTF(format, args); ++ va_end(args); ++} ++EXPORT_SYMBOL(DWC_PRINTF); ++ ++int DWC_SPRINTF(char *buffer, char *format, ...) ++{ ++ int retval; ++ va_list args; ++ va_start(args, format); ++ retval = vsprintf(buffer, format, args); ++ va_end(args); ++ return retval; ++} ++EXPORT_SYMBOL(DWC_SPRINTF); ++ ++int DWC_SNPRINTF(char *buffer, int size, char *format, ...) ++{ ++ int retval; ++ va_list args; ++ va_start(args, format); ++ retval = vsnprintf(buffer, size, format, args); ++ va_end(args); ++ return retval; ++} ++EXPORT_SYMBOL(DWC_SNPRINTF); ++ ++void __DWC_WARN(char *format, ...) ++{ ++ va_list args; ++ va_start(args, format); ++ DWC_PRINTF(KERN_WARNING); ++ DWC_VPRINTF(format, args); ++ va_end(args); ++} ++EXPORT_SYMBOL(__DWC_WARN); ++ ++void __DWC_ERROR(char *format, ...) ++{ ++ va_list args; ++ va_start(args, format); ++ DWC_PRINTF(KERN_ERR); ++ DWC_VPRINTF(format, args); ++ va_end(args); ++} ++EXPORT_SYMBOL(__DWC_ERROR); ++ ++void DWC_EXCEPTION(char *format, ...) ++{ ++ va_list args; ++ va_start(args, format); ++ DWC_PRINTF(KERN_ERR); ++ DWC_VPRINTF(format, args); ++ va_end(args); ++ BUG_ON(1); ++} ++EXPORT_SYMBOL(DWC_EXCEPTION); ++ ++#ifdef DEBUG ++void __DWC_DEBUG(char *format, ...) ++{ ++ va_list args; ++ va_start(args, format); ++ DWC_PRINTF(KERN_DEBUG); ++ DWC_VPRINTF(format, args); ++ va_end(args); ++} ++EXPORT_SYMBOL(__DWC_DEBUG); ++#endif ++ ++ ++ ++/* dwc_mem.h */ ++ ++#if 0 ++dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size, ++ uint32_t align, ++ uint32_t alloc) ++{ ++ struct dma_pool *pool = dma_pool_create("Pool", NULL, ++ size, align, alloc); ++ return (dwc_pool_t *)pool; ++} ++ ++void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool) ++{ ++ dma_pool_destroy((struct dma_pool *)pool); ++} ++ ++void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, U64 *dma_addr) ++{ ++ return dma_pool_alloc((struct dma_pool *)pool, GFP_KERNEL, dma_addr); ++} ++ ++void *DWC_DMA_POOL_ZALLOC(dwc_pool_t *pool, U64 *dma_addr) ++{ ++ void *vaddr = DWC_DMA_POOL_ALLOC(pool, dma_addr); ++ memset(); ++} ++ ++void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr) ++{ ++ dma_pool_free(pool, vaddr, daddr); ++} ++ ++#endif ++ ++void *__DWC_DMA_ALLOC(uint32_t size, dwc_dma_t *dma_addr) ++{ ++ void *buf = dma_alloc_coherent(NULL, (size_t)size, dma_addr, GFP_KERNEL); ++ if (!buf) { ++ return NULL; ++ } ++ memset(buf, 0, (size_t)size); ++ return buf; ++} ++EXPORT_SYMBOL(__DWC_DMA_ALLOC); ++ ++void __DWC_DMA_FREE(uint32_t size, void *virt_addr, dwc_dma_t dma_addr) ++{ ++ dma_free_coherent(NULL, size, virt_addr, dma_addr); ++} ++EXPORT_SYMBOL(__DWC_DMA_FREE); ++ ++void *__DWC_DMA_ALLOC_ATOMIC(uint32_t size, dwc_dma_t *dma_addr) ++{ ++ void *buf = dma_alloc_coherent(NULL, (size_t)size, dma_addr, GFP_ATOMIC); ++ if (!buf) { ++ return NULL; ++ } ++ memset(buf, 0, (size_t)size); ++ return buf; ++} ++EXPORT_SYMBOL(__DWC_DMA_ALLOC_ATOMIC); ++ ++void *__DWC_ALLOC(uint32_t size) ++{ ++ return kzalloc(size, GFP_KERNEL); ++} ++EXPORT_SYMBOL(__DWC_ALLOC); ++ ++void *__DWC_ALLOC_ATOMIC(uint32_t size) ++{ ++ return kzalloc(size, GFP_ATOMIC); ++} ++EXPORT_SYMBOL(__DWC_ALLOC_ATOMIC); ++ ++void __DWC_FREE(void *addr) ++{ ++ kfree(addr); ++} ++EXPORT_SYMBOL(__DWC_FREE); ++ ++/* dwc_crypto.h */ ++ ++void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length) ++{ ++ get_random_bytes(buffer, length); ++} ++EXPORT_SYMBOL(DWC_RANDOM_BYTES); ++ ++int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out) ++{ ++ struct crypto_blkcipher *tfm; ++ struct blkcipher_desc desc; ++ struct scatterlist sgd; ++ struct scatterlist sgs; ++ ++ tfm = crypto_alloc_blkcipher("cbc(aes)", 0, CRYPTO_ALG_ASYNC); ++ if (tfm == NULL) { ++ printk("failed to load transform for aes CBC\n"); ++ return -1; ++ } ++ ++ crypto_blkcipher_setkey(tfm, key, keylen); ++ crypto_blkcipher_set_iv(tfm, iv, 16); ++ ++ sg_init_one(&sgd, out, messagelen); ++ sg_init_one(&sgs, message, messagelen); ++ ++ desc.tfm = tfm; ++ desc.flags = 0; ++ ++ if(crypto_blkcipher_encrypt(&desc, &sgd, &sgs, messagelen)) { ++ crypto_free_blkcipher(tfm); ++ DWC_ERROR("AES CBC encryption failed"); ++ return -1; ++ } ++ ++ crypto_free_blkcipher(tfm); ++ return 0; ++} ++EXPORT_SYMBOL(DWC_AES_CBC); ++ ++int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out) ++{ ++ struct crypto_hash *tfm; ++ struct hash_desc desc; ++ struct scatterlist sg; ++ ++ tfm = crypto_alloc_hash("sha256", 0, CRYPTO_ALG_ASYNC); ++ if (IS_ERR(tfm)) { ++ DWC_ERROR("Failed to load transform for sha256: %ld\n", PTR_ERR(tfm)); ++ return 0; ++ } ++ desc.tfm = tfm; ++ desc.flags = 0; ++ ++ sg_init_one(&sg, message, len); ++ crypto_hash_digest(&desc, &sg, len, out); ++ crypto_free_hash(tfm); ++ ++ return 1; ++} ++EXPORT_SYMBOL(DWC_SHA256); ++ ++int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen, ++ uint8_t *key, uint32_t keylen, uint8_t *out) ++{ ++ struct crypto_hash *tfm; ++ struct hash_desc desc; ++ struct scatterlist sg; ++ ++ tfm = crypto_alloc_hash("hmac(sha256)", 0, CRYPTO_ALG_ASYNC); ++ if (IS_ERR(tfm)) { ++ DWC_ERROR("Failed to load transform for hmac(sha256): %ld\n", PTR_ERR(tfm)); ++ return 0; ++ } ++ desc.tfm = tfm; ++ desc.flags = 0; ++ ++ sg_init_one(&sg, message, messagelen); ++ crypto_hash_setkey(tfm, key, keylen); ++ crypto_hash_digest(&desc, &sg, messagelen, out); ++ crypto_free_hash(tfm); ++ ++ return 1; ++} ++EXPORT_SYMBOL(DWC_HMAC_SHA256); ++ ++/* Byte Ordering Conversions. */ ++uint32_t DWC_CPU_TO_LE32(void *p) ++{ ++#ifdef __LITTLE_ENDIAN ++ return *((uint32_t *)p); ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ ++ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24)); ++#endif ++} ++EXPORT_SYMBOL(DWC_CPU_TO_LE32); ++ ++uint32_t DWC_CPU_TO_BE32(void *p) ++{ ++#ifdef __BIG_ENDIAN ++ return *((uint32_t *)p); ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ ++ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24)); ++#endif ++} ++EXPORT_SYMBOL(DWC_CPU_TO_BE32); ++ ++uint32_t DWC_LE32_TO_CPU(void *p) ++{ ++#ifdef __LITTLE_ENDIAN ++ return *((uint32_t *)p); ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ ++ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24)); ++ ++#endif ++} ++EXPORT_SYMBOL(DWC_LE32_TO_CPU); ++ ++uint32_t DWC_BE32_TO_CPU(void *p) ++{ ++#ifdef __BIG_ENDIAN ++ return *((uint32_t *)p); ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ ++ return (u_p[3] | (u_p[2] << 8) | (u_p[1] << 16) | (u_p[0] << 24)); ++#endif ++} ++EXPORT_SYMBOL(DWC_BE32_TO_CPU); ++ ++uint16_t DWC_CPU_TO_LE16(void *p) ++{ ++#ifdef __LITTLE_ENDIAN ++ return *((uint16_t *)p); ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ return (u_p[1] | (u_p[0] << 8)); ++#endif ++} ++EXPORT_SYMBOL(DWC_CPU_TO_LE16); ++ ++uint16_t DWC_CPU_TO_BE16(void *p) ++{ ++#ifdef __BIG_ENDIAN ++ return *((uint16_t *)p); ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ return (u_p[1] | (u_p[0] << 8)); ++#endif ++} ++EXPORT_SYMBOL(DWC_CPU_TO_BE16); ++ ++uint16_t DWC_LE16_TO_CPU(void *p) ++{ ++#ifdef __LITTLE_ENDIAN ++ return *((uint16_t *)p); ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ return (u_p[1] | (u_p[0] << 8)); ++#endif ++} ++EXPORT_SYMBOL(DWC_LE16_TO_CPU); ++ ++uint16_t DWC_BE16_TO_CPU(void *p) ++{ ++#ifdef __BIG_ENDIAN ++ return *((uint16_t *p)p); ++#else ++ uint8_t *u_p = (uint8_t *)p; ++ return (u_p[1] | (u_p[0] << 8)); ++#endif ++} ++EXPORT_SYMBOL(DWC_BE16_TO_CPU); ++ ++ ++/* Registers */ ++ ++uint32_t DWC_READ_REG32(uint32_t volatile *reg) ++{ ++ return readl(reg); ++} ++EXPORT_SYMBOL(DWC_READ_REG32); ++ ++#if 0 ++uint64_t DWC_READ_REG64(uint64_t volatile *reg) ++{ ++} ++#endif ++ ++void DWC_WRITE_REG32(uint32_t volatile *reg, uint32_t value) ++{ ++ writel(value, reg); ++} ++EXPORT_SYMBOL(DWC_WRITE_REG32); ++ ++#if 0 ++void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value) ++{ ++} ++#endif ++ ++void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask) ++{ ++ writel( (readl(reg) & ~clear_mask) | set_mask, reg ); ++} ++EXPORT_SYMBOL(DWC_MODIFY_REG32); ++ ++#if 0 ++void DWC_MODIFY_REG64(uint64_t volatile *reg, uint64_t value) ++{ ++} ++#endif ++ ++ ++ ++/* Threading */ ++ ++typedef struct work_container ++{ ++ dwc_work_callback_t cb; ++ void *data; ++ dwc_workq_t *wq; ++ char *name; ++ ++#ifdef DEBUG ++ DWC_CIRCLEQ_ENTRY(work_container) entry; ++#endif ++ ++ struct delayed_work work; ++} work_container_t; ++ ++#ifdef DEBUG ++DWC_CIRCLEQ_HEAD(work_container_queue, work_container); ++#endif ++ ++struct dwc_workq ++{ ++ struct workqueue_struct *wq; ++ int pending; ++ dwc_spinlock_t *lock; ++ dwc_waitq_t *waitq; ++ ++#ifdef DEBUG ++ struct work_container_queue entries; ++#endif ++}; ++ ++static void do_work(struct work_struct *work) ++{ ++ int64_t flags; ++ struct delayed_work *dw = container_of(work, struct delayed_work, work); ++ work_container_t *container = container_of(dw, struct work_container, work); ++ dwc_workq_t *wq = container->wq; ++ ++ container->cb(container->data); ++ ++#ifdef DEBUG ++ DWC_CIRCLEQ_REMOVE(&wq->entries, container, entry); ++#endif ++ ++ if (container->name) { ++ DWC_DEBUG("Work done: %s, container=%p", ++ container->name, container); //GRAYG ++ DWC_FREE(container->name); ++ } ++ DWC_FREE(container); ++ ++ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags); ++ wq->pending --; ++ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags); ++ DWC_WAITQ_TRIGGER(wq->waitq); ++} ++ ++static int work_done(void *data) ++{ ++ dwc_workq_t *workq = (dwc_workq_t *)data; ++ return workq->pending == 0; ++} ++ ++int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout) ++{ ++ return DWC_WAITQ_WAIT_TIMEOUT(workq->waitq, work_done, workq, timeout); ++} ++EXPORT_SYMBOL(DWC_WORKQ_WAIT_WORK_DONE); ++ ++dwc_workq_t *DWC_WORKQ_ALLOC(char *name) ++{ ++ dwc_workq_t *wq = DWC_ALLOC(sizeof(*wq)); ++ wq->wq = create_singlethread_workqueue(name); ++ wq->pending = 0; ++ wq->lock = DWC_SPINLOCK_ALLOC(); ++ wq->waitq = DWC_WAITQ_ALLOC(); ++#ifdef DEBUG ++ DWC_CIRCLEQ_INIT(&wq->entries); ++#endif ++ return wq; ++} ++EXPORT_SYMBOL(DWC_WORKQ_ALLOC); ++ ++void DWC_WORKQ_FREE(dwc_workq_t *wq) ++{ ++#ifdef DEBUG ++ if (wq->pending != 0) { ++ struct work_container *wc; ++ DWC_ERROR("Destroying work queue with pending work"); ++ DWC_CIRCLEQ_FOREACH(wc, &wq->entries, entry) { ++ DWC_ERROR("Work %s still pending", wc->name); ++ } ++ } ++#endif ++ destroy_workqueue((struct workqueue_struct *)wq->wq); ++ DWC_SPINLOCK_FREE(wq->lock); ++ DWC_WAITQ_FREE(wq->waitq); ++ DWC_FREE(wq); ++} ++EXPORT_SYMBOL(DWC_WORKQ_FREE); ++ ++void DWC_WORKQ_SCHEDULE(dwc_workq_t *wq, dwc_work_callback_t work_cb, void *data, char *format, ...) ++{ ++ int64_t flags; ++ work_container_t *container; ++ static char name[128]; ++ ++ va_list args; ++ va_start(args, format); ++ if (format) ++ DWC_VSNPRINTF(name, 128, format, args); ++ va_end(args); ++ ++ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags); ++ wq->pending ++; ++ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags); ++ DWC_WAITQ_TRIGGER(wq->waitq); ++ ++ container = DWC_ALLOC_ATOMIC(sizeof(*container)); ++ ++ container->data = data; ++ container->cb = work_cb; ++ container->wq = wq; ++ if (format) { ++ container->name = DWC_STRDUP(name); ++ DWC_DEBUG("Queueing work: %s, contianer=%p", ++ container->name, container); ++ } else ++ container->name = NULL; ++ ++ INIT_WORK(&container->work.work, do_work); ++ ++#ifdef DEBUG ++ DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry); ++#endif ++ ++ queue_work(wq->wq, &container->work.work); ++ ++} ++EXPORT_SYMBOL(DWC_WORKQ_SCHEDULE); ++ ++void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *wq, dwc_work_callback_t work_cb, void *data, uint32_t time, char *format, ...) ++{ ++ int64_t flags; ++ work_container_t *container; ++ static char name[128]; ++ ++ va_list args; ++ va_start(args, format); ++ if (format) ++ DWC_VSNPRINTF(name, 128, format, args); ++ va_end(args); ++ ++ DWC_SPINLOCK_IRQSAVE(wq->lock, &flags); ++ wq->pending ++; ++ DWC_SPINUNLOCK_IRQRESTORE(wq->lock, flags); ++ DWC_WAITQ_TRIGGER(wq->waitq); ++ ++ container = DWC_ALLOC_ATOMIC(sizeof(*container)); ++ ++ container->data = data; ++ container->cb = work_cb; ++ container->wq = wq; ++ if (format) { //GRAYG ++ container->name = DWC_STRDUP(name); ++ DWC_DEBUG("Queueing work: %s, contianer=%p", ++ container->name, container); ++ } else ++ container->name = NULL; ++ INIT_DELAYED_WORK(&container->work, do_work); ++ ++#ifdef DEBUG ++ DWC_CIRCLEQ_INSERT_TAIL(&wq->entries, container, entry); ++#endif ++ ++ queue_delayed_work(wq->wq, &container->work, msecs_to_jiffies(time)); ++ ++} ++EXPORT_SYMBOL(DWC_WORKQ_SCHEDULE_DELAYED); ++ ++ ++int DWC_WORKQ_PENDING(dwc_workq_t *wq) ++{ ++ return wq->pending; ++} ++EXPORT_SYMBOL(DWC_WORKQ_PENDING); ++ ++dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void) ++{ ++ spinlock_t *sl = (spinlock_t *)1; ++#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP) ++ sl = DWC_ALLOC(sizeof(*sl)); ++ spin_lock_init(sl); ++#endif ++ return (dwc_spinlock_t *)sl; ++} ++EXPORT_SYMBOL(DWC_SPINLOCK_ALLOC); ++ ++void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock) ++{ ++#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP) ++ DWC_FREE(lock); ++#endif ++} ++EXPORT_SYMBOL(DWC_SPINLOCK_FREE); ++ ++void DWC_SPINLOCK(dwc_spinlock_t *lock) ++{ ++#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP) ++ spin_lock((spinlock_t *)lock); ++#endif ++} ++EXPORT_SYMBOL(DWC_SPINLOCK); ++ ++void DWC_SPINUNLOCK(dwc_spinlock_t *lock) ++{ ++#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP) ++ spin_unlock((spinlock_t *)lock); ++#endif ++} ++EXPORT_SYMBOL(DWC_SPINUNLOCK); ++ ++void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, uint64_t *flags) ++{ ++ unsigned long f; ++#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP) ++ spin_lock_irqsave((spinlock_t *)lock, f); ++#else ++ local_irq_save(f); ++#endif ++ *flags = f; ++} ++EXPORT_SYMBOL(DWC_SPINLOCK_IRQSAVE); ++ ++void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, uint64_t flags) ++{ ++#if defined(CONFIG_PREEMPT) || defined(CONFIG_SMP) ++ spin_unlock_irqrestore((spinlock_t *)lock, flags); ++#else ++ // in kernel 2.6.31, at least, we check for unsigned long ++ local_irq_restore((unsigned long)flags); ++#endif ++} ++EXPORT_SYMBOL(DWC_SPINUNLOCK_IRQRESTORE); ++ ++dwc_mutex_t *DWC_MUTEX_ALLOC(void) ++{ ++ dwc_mutex_t *mutex = (dwc_mutex_t*)DWC_ALLOC(sizeof(struct mutex)); ++ struct mutex *m = (struct mutex *)mutex; ++ mutex_init(m); ++ return mutex; ++} ++EXPORT_SYMBOL(DWC_MUTEX_ALLOC); ++ ++#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)) ++#else ++void DWC_MUTEX_FREE(dwc_mutex_t *mutex) ++{ ++ mutex_destroy((struct mutex *)mutex); ++ DWC_FREE(mutex); ++} ++EXPORT_SYMBOL(DWC_MUTEX_FREE); ++#endif ++ ++void DWC_MUTEX_LOCK(dwc_mutex_t *mutex) ++{ ++ struct mutex *m = (struct mutex *)mutex; ++ mutex_lock(m); ++} ++EXPORT_SYMBOL(DWC_MUTEX_LOCK); ++ ++int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex) ++{ ++ struct mutex *m = (struct mutex *)mutex; ++ return mutex_trylock(m); ++} ++EXPORT_SYMBOL(DWC_MUTEX_TRYLOCK); ++ ++void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex) ++{ ++ struct mutex *m = (struct mutex *)mutex; ++ mutex_unlock(m); ++} ++EXPORT_SYMBOL(DWC_MUTEX_UNLOCK); ++ ++dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t thread_function, char *name, void *data) ++{ ++ struct task_struct *thread = kthread_run(thread_function, data, name); ++ if (thread == ERR_PTR(-ENOMEM)) { ++ return NULL; ++ } ++ return (dwc_thread_t *)thread; ++} ++EXPORT_SYMBOL(DWC_THREAD_RUN); ++ ++int DWC_THREAD_STOP(dwc_thread_t *thread) ++{ ++ return kthread_stop((struct task_struct *)thread); ++} ++EXPORT_SYMBOL(DWC_THREAD_STOP); ++ ++dwc_bool_t DWC_THREAD_SHOULD_STOP() ++{ ++ return kthread_should_stop(); ++} ++EXPORT_SYMBOL(DWC_THREAD_SHOULD_STOP); ++ ++/* Timers */ ++ ++struct dwc_timer ++{ ++ struct timer_list *t; ++ char *name; ++ dwc_timer_callback_t cb; ++ void *data; ++ uint8_t scheduled; ++ dwc_spinlock_t *lock; ++}; ++ ++static void set_scheduled(dwc_timer_t *t, int s) ++{ ++ uint64_t flags; ++ DWC_SPINLOCK_IRQSAVE(t->lock, &flags); ++ t->scheduled = s; ++ DWC_SPINUNLOCK_IRQRESTORE(t->lock, flags); ++} ++ ++static int get_scheduled(dwc_timer_t *t) ++{ ++ int s; ++ uint64_t flags; ++ DWC_SPINLOCK_IRQSAVE(t->lock, &flags); ++ s = t->scheduled; ++ DWC_SPINUNLOCK_IRQRESTORE(t->lock, flags); ++ return s; ++} ++ ++static void timer_callback(unsigned long data) ++{ ++ dwc_timer_t *timer = (dwc_timer_t *)data; ++ set_scheduled(timer, 0); ++ DWC_DEBUG("Timer %s callback", timer->name); ++ timer->cb(timer->data); ++} ++ ++dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data) ++{ ++ dwc_timer_t *t = DWC_ALLOC(sizeof(*t)); ++ if (!t) { ++ DWC_ERROR("Cannot allocate memory for timer"); ++ return NULL; ++ } ++ t->t = DWC_ALLOC(sizeof(*t->t)); ++ if (!t->t) { ++ DWC_ERROR("Cannot allocate memory for timer->t"); ++ goto no_timer; ++ } ++ ++ t->name = DWC_STRDUP(name); ++ if (!t->name) { ++ DWC_ERROR("Cannot allocate memory for timer->name"); ++ goto no_name; ++ } ++ ++ t->lock = DWC_SPINLOCK_ALLOC(); ++ if (!t->lock) { ++ DWC_ERROR("Cannot allocate memory for lock"); ++ goto no_lock; ++ } ++ t->scheduled = 0; ++ t->t->base = &boot_tvec_bases; ++ t->t->expires = jiffies; ++ setup_timer(t->t, timer_callback, (unsigned long)t); ++ ++ t->cb = cb; ++ t->data = data; ++ ++ return t; ++ ++ no_lock: ++ DWC_FREE(t->name); ++ no_name: ++ DWC_FREE(t->t); ++ no_timer: ++ DWC_FREE(t); ++ return NULL; ++} ++EXPORT_SYMBOL(DWC_TIMER_ALLOC); ++ ++void DWC_TIMER_FREE(dwc_timer_t *timer) ++{ ++ if (get_scheduled(timer)) { ++ del_timer(timer->t); ++ } ++ ++ DWC_SPINLOCK_FREE(timer->lock); ++ DWC_FREE(timer->t); ++ DWC_FREE(timer->name); ++ DWC_FREE(timer); ++} ++EXPORT_SYMBOL(DWC_TIMER_FREE); ++ ++void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time) ++{ ++ if (!get_scheduled(timer)) { ++ set_scheduled(timer, 1); ++ //cgg: DWC_DEBUG("Scheduling timer %s to expire in +%d msec", timer->name, time); ++ timer->t->expires = jiffies + msecs_to_jiffies(time); ++ add_timer(timer->t); ++ } ++ else { ++ //cgg: DWC_DEBUG("Modifying timer %s to expire in +%d msec", timer->name, time); ++ mod_timer(timer->t, jiffies + msecs_to_jiffies(time)); ++ } ++} ++EXPORT_SYMBOL(DWC_TIMER_SCHEDULE); ++ ++void DWC_TIMER_CANCEL(dwc_timer_t *timer) ++{ ++ del_timer(timer->t); ++} ++EXPORT_SYMBOL(DWC_TIMER_CANCEL); ++ ++struct dwc_tasklet ++{ ++ struct tasklet_struct t; ++ dwc_tasklet_callback_t cb; ++ void *data; ++}; ++ ++static void tasklet_callback(unsigned long data) ++{ ++ dwc_tasklet_t *t = (dwc_tasklet_t *)data; ++ t->cb(t->data); ++} ++ ++dwc_tasklet_t *DWC_TASK_ALLOC(dwc_tasklet_callback_t cb, void *data) ++{ ++ dwc_tasklet_t *t = DWC_ALLOC(sizeof(*t)); ++ ++ if(t) { ++ t->data = data; ++ t->cb = cb; ++ tasklet_init(&t->t, tasklet_callback, (unsigned long)t); ++ } else { ++ DWC_ERROR("Cannot allocate memory for tasklet\n"); ++ } ++ ++ return t; ++} ++EXPORT_SYMBOL(DWC_TASK_ALLOC); ++ ++void DWC_TASK_FREE(dwc_tasklet_t *t) ++{ ++ DWC_FREE(t); ++} ++EXPORT_SYMBOL(DWC_TASK_FREE); ++ ++void DWC_TASK_SCHEDULE(dwc_tasklet_t *task) ++{ ++ tasklet_schedule(&task->t); ++} ++EXPORT_SYMBOL(DWC_TASK_SCHEDULE); ++ ++/* Timing */ ++ ++void DWC_UDELAY(uint32_t usecs) ++{ ++ udelay(usecs); ++} ++EXPORT_SYMBOL(DWC_UDELAY); ++ ++void DWC_MDELAY(uint32_t msecs) ++{ ++ mdelay(msecs); ++} ++EXPORT_SYMBOL(DWC_MDELAY); ++ ++void DWC_MSLEEP(uint32_t msecs) ++{ ++ msleep(msecs); ++} ++EXPORT_SYMBOL(DWC_MSLEEP); ++ ++uint32_t DWC_TIME(void) ++{ ++ return jiffies_to_msecs(jiffies); ++} ++EXPORT_SYMBOL(DWC_TIME); ++ ++ ++/* Wait Queues */ ++ ++struct dwc_waitq ++{ ++ wait_queue_head_t queue; ++ int abort; ++}; ++ ++dwc_waitq_t *DWC_WAITQ_ALLOC(void) ++{ ++ dwc_waitq_t *wq = DWC_ALLOC(sizeof(*wq)); ++ init_waitqueue_head(&wq->queue); ++ wq->abort = 0; ++ return wq; ++} ++EXPORT_SYMBOL(DWC_WAITQ_ALLOC); ++ ++void DWC_WAITQ_FREE(dwc_waitq_t *wq) ++{ ++ DWC_FREE(wq); ++} ++EXPORT_SYMBOL(DWC_WAITQ_FREE); ++ ++static int32_t check_result(dwc_waitq_t *wq, int result) ++{ int32_t msecs; ++ if (result > 0) { ++ msecs = jiffies_to_msecs(result); ++ if (!msecs) { ++ return 1; ++ } ++ return msecs; ++ } ++ ++ if (result == 0) { ++ return -DWC_E_TIMEOUT; ++ } ++ ++ if ((result == -ERESTARTSYS) || (wq->abort == 1)) { ++ return -DWC_E_ABORT; ++ } ++ ++ return -DWC_E_UNKNOWN; ++} ++ ++int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t condition, void *data) ++{ ++ int result = wait_event_interruptible(wq->queue, ++ condition(data) || wq->abort); ++ return check_result(wq, result); ++} ++EXPORT_SYMBOL(DWC_WAITQ_WAIT); ++ ++int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t condition, ++ void *data, int32_t msecs) ++{ ++ int result = wait_event_interruptible_timeout(wq->queue, ++ condition(data) || wq->abort, ++ msecs_to_jiffies(msecs)); ++ return check_result(wq, result); ++} ++EXPORT_SYMBOL(DWC_WAITQ_WAIT_TIMEOUT); ++ ++void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq) ++{ ++ wake_up_interruptible(&wq->queue); ++} ++EXPORT_SYMBOL(DWC_WAITQ_TRIGGER); ++ ++void DWC_WAITQ_ABORT(dwc_waitq_t *wq) ++{ ++ wq->abort = 1; ++ DWC_WAITQ_TRIGGER(wq); ++} ++EXPORT_SYMBOL(DWC_WAITQ_ABORT); +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_crypto.c +@@ -0,0 +1,306 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port/dwc_crypto.c $ ++ * $Revision: #1 $ ++ * $Date: 2008/12/21 $ ++ * $Change: 1156609 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++ ++/** @file ++ * This file contains the WUSB cryptographic routines. ++ */ ++ ++#include "dwc_crypto.h" ++#include "usb.h" ++ ++#ifdef DEBUG ++static inline void dump_bytes(char *name, uint8_t *bytes, int len) ++{ ++ int i; ++ DWC_PRINTF("%s: ", name); ++ for (i=0; i<len; i++) { ++ DWC_PRINTF("%02x ", bytes[i]); ++ } ++ DWC_PRINTF("\n"); ++} ++#else ++#define dump_bytes(x...) ++#endif ++ ++/* Display a block */ ++void show_block(const u8 *blk, const char *prefix, const char *suffix, int a) ++{ ++#ifdef DEBUG_CRYPTO ++ int i, blksize = 16; ++ ++ DWC_DEBUG("%s", prefix); ++ ++ if (suffix == NULL) { ++ suffix = "\n"; ++ blksize = a; ++ } ++ ++ for (i = 0; i < blksize; i++) ++ DWC_PRINT("%02x%s", *blk++, ((i & 3) == 3) ? " " : " "); ++ DWC_PRINT(suffix); ++ ++#endif ++} ++ ++/** ++ * Encrypts an array of bytes using the AES encryption engine. ++ * If <code>dst</code> == <code>src</code>, then the bytes will be encrypted ++ * in-place. ++ * ++ * @return 0 on success, negative error code on error. ++ */ ++int dwc_wusb_aes_encrypt(u8 *src, u8 *key, u8 *dst) ++{ ++ u8 block_t[16]; ++ DWC_MEMSET(block_t, 0, 16); ++ ++ return DWC_AES_CBC(src, 16, key, 16, block_t, dst); ++} ++ ++/** ++ * The CCM-MAC-FUNCTION described in section 6.5 of the WUSB spec. ++ * This function takes a data string and returns the encrypted CBC ++ * Counter-mode MIC. ++ * ++ * @param key The 128-bit symmetric key. ++ * @param nonce The CCM nonce. ++ * @param label The unique 14-byte ASCII text label. ++ * @param bytes The byte array to be encrypted. ++ * @param len Length of the byte array. ++ * @param result Byte array to receive the 8-byte encrypted MIC. ++ */ ++void dwc_wusb_cmf(u8 *key, u8 *nonce, ++ char *label, u8 *bytes, int len, u8 *result) ++{ ++ u8 block_m[16]; ++ u8 block_x[16]; ++ u8 block_t[8]; ++ int idx, blkNum; ++ u16 la = (u16)(len + 14); ++ ++ /* Set the AES-128 key */ ++ //dwc_aes_setkey(tfm, key, 16); ++ ++ /* Fill block B0 from flags = 0x59, N, and l(m) = 0 */ ++ block_m[0] = 0x59; ++ for (idx = 0; idx < 13; idx++) ++ block_m[idx + 1] = nonce[idx]; ++ block_m[14] = 0; ++ block_m[15] = 0; ++ ++ /* Produce the CBC IV */ ++ dwc_wusb_aes_encrypt(block_m, key, block_x); ++ show_block(block_m, "CBC IV in: ", "\n", 0); ++ show_block(block_x, "CBC IV out:", "\n", 0); ++ ++ /* Fill block B1 from l(a) = Blen + 14, and A */ ++ block_x[0] ^= (u8)(la >> 8); ++ block_x[1] ^= (u8)la; ++ for (idx = 0; idx < 14; idx++) ++ block_x[idx + 2] ^= label[idx]; ++ show_block(block_x, "After xor: ", "b1\n", 16); ++ ++ dwc_wusb_aes_encrypt(block_x, key, block_x); ++ show_block(block_x, "After AES: ", "b1\n", 16); ++ ++ idx = 0; ++ blkNum = 0; ++ ++ /* Fill remaining blocks with B */ ++ while (len-- > 0) { ++ block_x[idx] ^= *bytes++; ++ if (++idx >= 16) { ++ idx = 0; ++ show_block(block_x, "After xor: ", "\n", blkNum); ++ dwc_wusb_aes_encrypt(block_x, key, block_x); ++ show_block(block_x, "After AES: ", "\n", blkNum); ++ blkNum++; ++ } ++ } ++ ++ /* Handle partial last block */ ++ if (idx > 0) { ++ show_block(block_x, "After xor: ", "\n", blkNum); ++ dwc_wusb_aes_encrypt(block_x, key, block_x); ++ show_block(block_x, "After AES: ", "\n", blkNum); ++ } ++ ++ /* Save the MIC tag */ ++ DWC_MEMCPY(block_t, block_x, 8); ++ show_block(block_t, "MIC tag : ", NULL, 8); ++ ++ /* Fill block A0 from flags = 0x01, N, and counter = 0 */ ++ block_m[0] = 0x01; ++ block_m[14] = 0; ++ block_m[15] = 0; ++ ++ /* Encrypt the counter */ ++ dwc_wusb_aes_encrypt(block_m, key, block_x); ++ show_block(block_x, "CTR[MIC] : ", NULL, 8); ++ ++ /* XOR with MIC tag */ ++ for (idx = 0; idx < 8; idx++) { ++ block_t[idx] ^= block_x[idx]; ++ } ++ ++ /* Return result to caller */ ++ DWC_MEMCPY(result, block_t, 8); ++ show_block(result, "CCM-MIC : ", NULL, 8); ++ ++} ++ ++/** ++ * The PRF function described in section 6.5 of the WUSB spec. This function ++ * concatenates MIC values returned from dwc_cmf() to create a value of ++ * the requested length. ++ * ++ * @param prf_len Length of the PRF function in bits (64, 128, or 256). ++ * @param key, nonce, label, bytes, len Same as for dwc_cmf(). ++ * @param result Byte array to receive the result. ++ */ ++void dwc_wusb_prf(int prf_len, u8 *key, ++ u8 *nonce, char *label, u8 *bytes, int len, u8 *result) ++{ ++ int i; ++ ++ nonce[0] = 0; ++ for (i = 0; i < prf_len >> 6; i++, nonce[0]++) { ++ dwc_wusb_cmf(key, nonce, label, bytes, len, result); ++ result += 8; ++ } ++} ++ ++/** ++ * Fills in CCM Nonce per the WUSB spec. ++ * ++ * @param[in] haddr Host address. ++ * @param[in] daddr Device address. ++ * @param[in] tkid Session Key(PTK) identifier. ++ * @param[out] nonce Pointer to where the CCM Nonce output is to be written. ++ */ ++void dwc_wusb_fill_ccm_nonce(uint16_t haddr, uint16_t daddr, uint8_t *tkid, ++ uint8_t *nonce) ++{ ++ ++ DWC_DEBUG("%s %x %x\n", __func__, daddr, haddr); ++ ++ DWC_MEMSET(&nonce[0], 0, 16); ++ ++ DWC_MEMCPY(&nonce[6], tkid, 3); ++ nonce[9] = daddr & 0xFF; ++ nonce[10] = (daddr >> 8) & 0xFF; ++ nonce[11] = haddr & 0xFF; ++ nonce[12] = (haddr >> 8) & 0xFF; ++ ++ dump_bytes("CCM nonce", nonce, 16); ++} ++ ++/** ++ * Generates a 16-byte cryptographic-grade random number for the Host/Device ++ * Nonce. ++ */ ++void dwc_wusb_gen_nonce(uint16_t addr, uint8_t *nonce) ++{ ++ uint8_t inonce[16]; ++ uint32_t temp[4]; ++ ++ /* Fill in the Nonce */ ++ DWC_MEMSET(&inonce[0], 0, sizeof(inonce)); ++ inonce[9] = addr & 0xFF; ++ inonce[10] = (addr >> 8) & 0xFF; ++ inonce[11] = inonce[9]; ++ inonce[12] = inonce[10]; ++ ++ /* Collect "randomness samples" */ ++ DWC_RANDOM_BYTES((uint8_t *)temp, 16); ++ ++ dwc_wusb_prf_128((uint8_t *)temp, nonce, ++ "Random Numbers", (uint8_t *)temp, sizeof(temp), ++ nonce); ++} ++ ++/** ++ * Generates the Session Key (PTK) and Key Confirmation Key (KCK) per the ++ * WUSB spec. ++ * ++ * @param[in] ccm_nonce Pointer to CCM Nonce. ++ * @param[in] mk Master Key to derive the session from ++ * @param[in] hnonce Pointer to Host Nonce. ++ * @param[in] dnonce Pointer to Device Nonce. ++ * @param[out] kck Pointer to where the KCK output is to be written. ++ * @param[out] ptk Pointer to where the PTK output is to be written. ++ */ ++void dwc_wusb_gen_key(uint8_t *ccm_nonce, uint8_t *mk, uint8_t *hnonce, ++ uint8_t *dnonce, uint8_t *kck, uint8_t *ptk) ++{ ++ uint8_t idata[32]; ++ uint8_t odata[32]; ++ ++ dump_bytes("ck", mk, 16); ++ dump_bytes("hnonce", hnonce, 16); ++ dump_bytes("dnonce", dnonce, 16); ++ ++ /* The data is the HNonce and DNonce concatenated */ ++ DWC_MEMCPY(&idata[0], hnonce, 16); ++ DWC_MEMCPY(&idata[16], dnonce, 16); ++ ++ dwc_wusb_prf_256(mk, ccm_nonce, "Pair-wise keys", idata, 32, odata); ++ ++ /* Low 16 bytes of the result is the KCK, high 16 is the PTK */ ++ DWC_MEMCPY(kck, &odata[0], 16); ++ DWC_MEMCPY(ptk, &odata[16], 16); ++ ++ dump_bytes("kck", kck, 16); ++ dump_bytes("ptk", ptk, 16); ++} ++ ++/** ++ * Generates the Message Integrity Code over the Handshake data per the ++ * WUSB spec. ++ * ++ * @param ccm_nonce Pointer to CCM Nonce. ++ * @param kck Pointer to Key Confirmation Key. ++ * @param data Pointer to Handshake data to be checked. ++ * @param mic Pointer to where the MIC output is to be written. ++ */ ++void dwc_wusb_gen_mic(uint8_t *ccm_nonce, uint8_t *kck, ++ uint8_t *data, uint8_t *mic) ++{ ++ ++ dwc_wusb_prf_64(kck, ccm_nonce, "out-of-bandMIC", ++ data, WUSB_HANDSHAKE_LEN_FOR_MIC, mic); ++} ++ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_crypto.h +@@ -0,0 +1,103 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port/dwc_crypto.h $ ++ * $Revision: #1 $ ++ * $Date: 2008/12/21 $ ++ * $Change: 1156609 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++ ++#ifndef _DWC_CRYPTO_H_ ++#define _DWC_CRYPTO_H_ ++ ++/** @file ++ * ++ * This file contains declarations for the WUSB Cryptographic routines as ++ * defined in the WUSB spec. They are only to be used internally by the DWC UWB ++ * modules. ++ */ ++ ++#include "dwc_os.h" ++ ++int dwc_wusb_aes_encrypt(u8 *src, u8 *key, u8 *dst); ++ ++void dwc_wusb_cmf(u8 *key, u8 *nonce, ++ char *label, u8 *bytes, int len, u8 *result); ++void dwc_wusb_prf(int prf_len, u8 *key, ++ u8 *nonce, char *label, u8 *bytes, int len, u8 *result); ++ ++/** ++ * The PRF-64 function described in section 6.5 of the WUSB spec. ++ * ++ * @param key, nonce, label, bytes, len, result Same as for dwc_prf(). ++ */ ++static inline void dwc_wusb_prf_64(u8 *key, u8 *nonce, ++ char *label, u8 *bytes, int len, u8 *result) ++{ ++ dwc_wusb_prf(64, key, nonce, label, bytes, len, result); ++} ++ ++/** ++ * The PRF-128 function described in section 6.5 of the WUSB spec. ++ * ++ * @param key, nonce, label, bytes, len, result Same as for dwc_prf(). ++ */ ++static inline void dwc_wusb_prf_128(u8 *key, u8 *nonce, ++ char *label, u8 *bytes, int len, u8 *result) ++{ ++ dwc_wusb_prf(128, key, nonce, label, bytes, len, result); ++} ++ ++/** ++ * The PRF-256 function described in section 6.5 of the WUSB spec. ++ * ++ * @param key, nonce, label, bytes, len, result Same as for dwc_prf(). ++ */ ++static inline void dwc_wusb_prf_256(u8 *key, u8 *nonce, ++ char *label, u8 *bytes, int len, u8 *result) ++{ ++ dwc_wusb_prf(256, key, nonce, label, bytes, len, result); ++} ++ ++ ++void dwc_wusb_fill_ccm_nonce(uint16_t haddr, uint16_t daddr, uint8_t *tkid, ++ uint8_t *nonce); ++void dwc_wusb_gen_nonce(uint16_t addr, ++ uint8_t *nonce); ++ ++void dwc_wusb_gen_key(uint8_t *ccm_nonce, uint8_t *mk, ++ uint8_t *hnonce, uint8_t *dnonce, ++ uint8_t *kck, uint8_t *ptk); ++ ++ ++void dwc_wusb_gen_mic(uint8_t *ccm_nonce, uint8_t ++ *kck, uint8_t *data, uint8_t *mic); ++ ++#endif /* _DWC_CRYPTO_H_ */ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_dh.c +@@ -0,0 +1,286 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port/dwc_dh.c $ ++ * $Revision: #1 $ ++ * $Date: 2008/12/21 $ ++ * $Change: 1156609 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++#ifndef CONFIG_MACH_IPMATE ++#include "dwc_dh.h" ++#include "dwc_modpow.h" ++ ++#ifdef DEBUG ++/* This function prints out a buffer in the format described in the Association ++ * Model specification. */ ++static void dh_dump(char *str, void *_num, int len) ++{ ++ uint8_t *num = _num; ++ int i; ++ DWC_PRINTF("%s\n", str); ++ for (i = 0; i < len; i ++) { ++ DWC_PRINTF("%02x", num[i]); ++ if (((i + 1) % 2) == 0) DWC_PRINTF(" "); ++ if (((i + 1) % 26) == 0) DWC_PRINTF("\n"); ++ } ++ ++ DWC_PRINTF("\n"); ++} ++#else ++#define dh_dump(_x...) do {; } while(0) ++#endif ++ ++/* Constant g value */ ++static __u32 dh_g[] = { ++ 0x02000000, ++}; ++ ++/* Constant p value */ ++static __u32 dh_p[] = { ++ 0xFFFFFFFF, 0xFFFFFFFF, 0xA2DA0FC9, 0x34C26821, 0x8B62C6C4, 0xD11CDC80, 0x084E0229, 0x74CC678A, ++ 0xA6BE0B02, 0x229B133B, 0x79084A51, 0xDD04348E, 0xB31995EF, 0x1B433ACD, 0x6D0A2B30, 0x37145FF2, ++ 0x6D35E14F, 0x45C2516D, 0x76B585E4, 0xC67E5E62, 0xE9424CF4, 0x6BED37A6, 0xB65CFF0B, 0xEDB706F4, ++ 0xFB6B38EE, 0xA59F895A, 0x11249FAE, 0xE61F4B7C, 0x51662849, 0x3D5BE4EC, 0xB87C00C2, 0x05BF63A1, ++ 0x3648DA98, 0x9AD3551C, 0xA83F1669, 0x5FCF24FD, 0x235D6583, 0x96ADA3DC, 0x56F3621C, 0xBB528520, ++ 0x0729D59E, 0x6D969670, 0x4E350C67, 0x0498BC4A, 0x086C74F1, 0x7C2118CA, 0x465E9032, 0x3BCE362E, ++ 0x2C779EE3, 0x03860E18, 0xA283279B, 0x8FA207EC, 0xF05DC5B5, 0xC9524C6F, 0xF6CB2BDE, 0x18175895, ++ 0x7C499539, 0xE56A95EA, 0x1826D215, 0x1005FA98, 0x5A8E7215, 0x2DC4AA8A, 0x0D1733AD, 0x337A5004, ++ 0xAB2155A8, 0x64BA1CDF, 0x0485FBEC, 0x0AEFDB58, 0x5771EA8A, 0x7D0C065D, 0x850F97B3, 0xC7E4E1A6, ++ 0x8CAEF5AB, 0xD73309DB, 0xE0948C1E, 0x9D61254A, 0x26D2E3CE, 0x6BEED21A, 0x06FA2FF1, 0x64088AD9, ++ 0x730276D8, 0x646AC83E, 0x182B1F52, 0x0C207B17, 0x5717E1BB, 0x6C5D617A, 0xC0880977, 0xE246D9BA, ++ 0xA04FE208, 0x31ABE574, 0xFC5BDB43, 0x8E10FDE0, 0x20D1824B, 0xCAD23AA9, 0xFFFFFFFF, 0xFFFFFFFF, ++}; ++ ++static void dh_swap_bytes(void *_in, void *_out, uint32_t len) ++{ ++ uint8_t *in = _in; ++ uint8_t *out = _out; ++ int i; ++ for (i=0; i<len; i++) { ++ out[i] = in[len-1-i]; ++ } ++} ++ ++/* Computes the modular exponentiation (num^exp % mod). num, exp, and mod are ++ * big endian numbers of size len, in bytes. Each len value must be a multiple ++ * of 4. */ ++int dwc_dh_modpow(void *num, uint32_t num_len, ++ void *exp, uint32_t exp_len, ++ void *mod, uint32_t mod_len, ++ void *out) ++{ ++ /* modpow() takes little endian numbers. AM uses big-endian. This ++ * function swaps bytes of numbers before passing onto modpow. */ ++ ++ int retval = 0; ++ uint32_t *result; ++ ++ uint32_t *bignum_num = DWC_ALLOC(num_len + 4); ++ uint32_t *bignum_exp = DWC_ALLOC(exp_len + 4); ++ uint32_t *bignum_mod = DWC_ALLOC(mod_len + 4); ++ ++ dh_swap_bytes(num, &bignum_num[1], num_len); ++ bignum_num[0] = num_len / 4; ++ ++ dh_swap_bytes(exp, &bignum_exp[1], exp_len); ++ bignum_exp[0] = exp_len / 4; ++ ++ dh_swap_bytes(mod, &bignum_mod[1], mod_len); ++ bignum_mod[0] = mod_len / 4; ++ ++ result = dwc_modpow(bignum_num, bignum_exp, bignum_mod); ++ if (!result) { ++ retval = -1; ++ goto dh_modpow_nomem; ++ } ++ ++ dh_swap_bytes(&result[1], out, result[0] * 4); ++ DWC_FREE(result); ++ ++ dh_modpow_nomem: ++ DWC_FREE(bignum_num); ++ DWC_FREE(bignum_exp); ++ DWC_FREE(bignum_mod); ++ return retval; ++} ++ ++ ++int dwc_dh_pk(uint8_t nd, uint8_t *exp, uint8_t *pk, uint8_t *hash) ++{ ++ int retval; ++ uint8_t m3[385]; ++ ++#ifndef DH_TEST_VECTORS ++ DWC_RANDOM_BYTES(exp, 32); ++#endif ++ ++ /* Compute the pkd */ ++ if ((retval = dwc_dh_modpow(dh_g, 4, ++ exp, 32, ++ dh_p, 384, pk))) { ++ return retval; ++ } ++ ++ m3[384] = nd; ++ DWC_MEMCPY(&m3[0], pk, 384); ++ DWC_SHA256(m3, 385, hash); ++ ++ dh_dump("PK", pk, 384); ++ dh_dump("SHA-256(M3)", hash, 32); ++ return 0; ++} ++ ++int dwc_dh_derive_keys(uint8_t nd, uint8_t *pkh, uint8_t *pkd, ++ uint8_t *exp, int is_host, ++ char *dd, uint8_t *ck, uint8_t *kdk) ++{ ++ int retval; ++ uint8_t mv[784]; ++ uint8_t sha_result[32]; ++ uint8_t dhkey[384]; ++ uint8_t shared_secret[384]; ++ char *message; ++ uint32_t vd; ++ ++ uint8_t *pk; ++ ++ if (is_host) { ++ pk = pkd; ++ } ++ else { ++ pk = pkh; ++ } ++ ++ if ((retval = dwc_dh_modpow(pk, 384, ++ exp, 32, ++ dh_p, 384, shared_secret))) { ++ return retval; ++ } ++ dh_dump("Shared Secret", shared_secret, 384); ++ ++ DWC_SHA256(shared_secret, 384, dhkey); ++ dh_dump("DHKEY", dhkey, 384); ++ ++ DWC_MEMCPY(&mv[0], pkd, 384); ++ DWC_MEMCPY(&mv[384], pkh, 384); ++ DWC_MEMCPY(&mv[768], "displayed digest", 16); ++ dh_dump("MV", mv, 784); ++ ++ DWC_SHA256(mv, 784, sha_result); ++ dh_dump("SHA-256(MV)", sha_result, 32); ++ dh_dump("First 32-bits of SHA-256(MV)", sha_result, 4); ++ ++ dh_swap_bytes(sha_result, &vd, 4); ++#ifdef DEBUG ++ DWC_PRINTF("Vd (decimal) = %d\n", vd); ++#endif ++ ++ switch (nd) { ++ case 2: ++ vd = vd % 100; ++ DWC_SPRINTF(dd, "%02d", vd); ++ break; ++ case 3: ++ vd = vd % 1000; ++ DWC_SPRINTF(dd, "%03d", vd); ++ break; ++ case 4: ++ vd = vd % 10000; ++ DWC_SPRINTF(dd, "%04d", vd); ++ break; ++ } ++#ifdef DEBUG ++ DWC_PRINTF("Display Digits: %s\n", dd); ++#endif ++ ++ message = "connection key"; ++ DWC_HMAC_SHA256(message, DWC_STRLEN(message), dhkey, 32, sha_result); ++ dh_dump("HMAC(SHA-256, DHKey, connection key)", sha_result, 32); ++ DWC_MEMCPY(ck, sha_result, 16); ++ ++ message = "key derivation key"; ++ DWC_HMAC_SHA256(message, DWC_STRLEN(message), dhkey, 32, sha_result); ++ dh_dump("HMAC(SHA-256, DHKey, key derivation key)", sha_result, 32); ++ DWC_MEMCPY(kdk, sha_result, 32); ++ ++ return 0; ++} ++ ++ ++#ifdef DH_TEST_VECTORS ++ ++static __u8 dh_a[] = { ++ 0x44, 0x00, 0x51, 0xd6, ++ 0xf0, 0xb5, 0x5e, 0xa9, ++ 0x67, 0xab, 0x31, 0xc6, ++ 0x8a, 0x8b, 0x5e, 0x37, ++ 0xd9, 0x10, 0xda, 0xe0, ++ 0xe2, 0xd4, 0x59, 0xa4, ++ 0x86, 0x45, 0x9c, 0xaa, ++ 0xdf, 0x36, 0x75, 0x16, ++}; ++ ++static __u8 dh_b[] = { ++ 0x5d, 0xae, 0xc7, 0x86, ++ 0x79, 0x80, 0xa3, 0x24, ++ 0x8c, 0xe3, 0x57, 0x8f, ++ 0xc7, 0x5f, 0x1b, 0x0f, ++ 0x2d, 0xf8, 0x9d, 0x30, ++ 0x6f, 0xa4, 0x52, 0xcd, ++ 0xe0, 0x7a, 0x04, 0x8a, ++ 0xde, 0xd9, 0x26, 0x56, ++}; ++ ++void dwc_run_dh_test_vectors(void) ++{ ++ uint8_t pkd[384]; ++ uint8_t pkh[384]; ++ uint8_t hashd[32]; ++ uint8_t hashh[32]; ++ uint8_t ck[16]; ++ uint8_t kdk[32]; ++ char dd[5]; ++ ++ DWC_PRINTF("\n\n\nDH_TEST_VECTORS\n\n"); ++ ++ /* compute the PKd and SHA-256(PKd || Nd) */ ++ DWC_PRINTF("Computing PKd\n"); ++ dwc_dh_pk(2, dh_a, pkd, hashd); ++ ++ /* compute the PKd and SHA-256(PKh || Nd) */ ++ DWC_PRINTF("Computing PKh\n"); ++ dwc_dh_pk(2, dh_b, pkh, hashh); ++ ++ /* compute the dhkey */ ++ dwc_dh_derive_keys(2, pkh, pkd, dh_a, 0, dd, ck, kdk); ++} ++#endif /* DH_TEST_VECTORS */ ++ ++#endif /* CONFIG_IPMATE_MACH */ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_dh.h +@@ -0,0 +1,98 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port/dwc_dh.h $ ++ * $Revision: #1 $ ++ * $Date: 2008/12/21 $ ++ * $Change: 1156609 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++#ifndef _DWC_DH_H_ ++#define _DWC_DH_H_ ++ ++#include "dwc_os.h" ++ ++/** @file ++ * ++ * This file defines the common functions on device and host for performing ++ * numeric association as defined in the WUSB spec. They are only to be ++ * used internally by the DWC UWB modules. */ ++ ++extern int dwc_dh_sha256(uint8_t *message, uint32_t len, uint8_t *out); ++extern int dwc_dh_hmac_sha256(uint8_t *message, uint32_t messagelen, ++ uint8_t *key, uint32_t keylen, ++ uint8_t *out); ++extern int dwc_dh_modpow(void *num, uint32_t num_len, ++ void *exp, uint32_t exp_len, ++ void *mod, uint32_t mod_len, ++ void *out); ++ ++/** Computes PKD or PKH, and SHA-256(PKd || Nd) ++ * ++ * PK = g^exp mod p. ++ * ++ * Input: ++ * Nd = Number of digits on the device. ++ * ++ * Output: ++ * exp = A 32-byte buffer to be filled with a randomly generated number. ++ * used as either A or B. ++ * pk = A 384-byte buffer to be filled with the PKH or PKD. ++ * hash = A 32-byte buffer to be filled with SHA-256(PK || ND). ++ */ ++extern int dwc_dh_pk(uint8_t nd, uint8_t *exp, uint8_t *pkd, uint8_t *hash); ++ ++/** Computes the DHKEY, and VD. ++ * ++ * If called from host, then it will comput DHKEY=PKD^exp % p. ++ * If called from device, then it will comput DHKEY=PKH^exp % p. ++ * ++ * Input: ++ * pkd = The PKD value. ++ * pkh = The PKH value. ++ * exp = The A value (if device) or B value (if host) generated in dwc_wudev_dh_pk. ++ * is_host = Set to non zero if a WUSB host is calling this function. ++ * ++ * Output: ++ ++ * dd = A pointer to an buffer to be set to the displayed digits string to be shown ++ * to the user. This buffer should be at 5 bytes long to hold 4 digits plus a ++ * null termination character. This buffer can be used directly for display. ++ * ck = A 16-byte buffer to be filled with the CK. ++ * kdk = A 32-byte buffer to be filled with the KDK. ++ */ ++extern int dwc_dh_derive_keys(uint8_t nd, uint8_t *pkh, uint8_t *pkd, ++ uint8_t *exp, int is_host, ++ char *dd, uint8_t *ck, uint8_t *kdk); ++ ++#ifdef DH_TEST_VECTORS ++extern void dwc_run_dh_test_vectors(void); ++#endif ++ ++#endif /* _DWC_DH_H_ */ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_list.h +@@ -0,0 +1,616 @@ ++/* $OpenBSD: queue.h,v 1.26 2004/05/04 16:59:32 grange Exp $ */ ++/* $NetBSD: queue.h,v 1.11 1996/05/16 05:17:14 mycroft Exp $ */ ++ ++/* ++ * Copyright (c) 1991, 1993 ++ * The Regents of the University of California. All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. Neither the name of the University nor the names of its contributors ++ * may be used to endorse or promote products derived from this software ++ * without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND ++ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE ++ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL ++ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS ++ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ++ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF ++ * SUCH DAMAGE. ++ * ++ * @(#)queue.h 8.5 (Berkeley) 8/20/94 ++ */ ++ ++#ifndef _SYS_QUEUE_H_ ++#define _SYS_QUEUE_H_ ++ ++ ++/** @file ++ * ++ * This file defines linked list operations. It is derived from BSD with ++ * only the MACRO names being prefixed with DWC_. This is because a few of ++ * these names conflict with those on Linux. For documentation on use, see the ++ * inline comments in the source code. The original license for this source ++ * code applies and is preserved in the dwc_list.h source file. ++ */ ++ ++/* ++ * This file defines five types of data structures: singly-linked lists, ++ * lists, simple queues, tail queues, and circular queues. ++ * ++ * ++ * A singly-linked list is headed by a single forward pointer. The elements ++ * are singly linked for minimum space and pointer manipulation overhead at ++ * the expense of O(n) removal for arbitrary elements. New elements can be ++ * added to the list after an existing element or at the head of the list. ++ * Elements being removed from the head of the list should use the explicit ++ * macro for this purpose for optimum efficiency. A singly-linked list may ++ * only be traversed in the forward direction. Singly-linked lists are ideal ++ * for applications with large datasets and few or no removals or for ++ * implementing a LIFO queue. ++ * ++ * A list is headed by a single forward pointer (or an array of forward ++ * pointers for a hash table header). The elements are doubly linked ++ * so that an arbitrary element can be removed without a need to ++ * traverse the list. New elements can be added to the list before ++ * or after an existing element or at the head of the list. A list ++ * may only be traversed in the forward direction. ++ * ++ * A simple queue is headed by a pair of pointers, one the head of the ++ * list and the other to the tail of the list. The elements are singly ++ * linked to save space, so elements can only be removed from the ++ * head of the list. New elements can be added to the list before or after ++ * an existing element, at the head of the list, or at the end of the ++ * list. A simple queue may only be traversed in the forward direction. ++ * ++ * A tail queue is headed by a pair of pointers, one to the head of the ++ * list and the other to the tail of the list. The elements are doubly ++ * linked so that an arbitrary element can be removed without a need to ++ * traverse the list. New elements can be added to the list before or ++ * after an existing element, at the head of the list, or at the end of ++ * the list. A tail queue may be traversed in either direction. ++ * ++ * A circle queue is headed by a pair of pointers, one to the head of the ++ * list and the other to the tail of the list. The elements are doubly ++ * linked so that an arbitrary element can be removed without a need to ++ * traverse the list. New elements can be added to the list before or after ++ * an existing element, at the head of the list, or at the end of the list. ++ * A circle queue may be traversed in either direction, but has a more ++ * complex end of list detection. ++ * ++ * For details on the use of these macros, see the queue(3) manual page. ++ */ ++ ++/* ++ * Double-linked List. ++ */ ++ ++typedef struct dwc_list_link { ++ struct dwc_list_link *next; ++ struct dwc_list_link *prev; ++} dwc_list_link_t; ++ ++#define DWC_LIST_INIT(link) do{ \ ++ (link)->next = (link); \ ++ (link)->prev = (link); \ ++} while(0) ++ ++#define DWC_LIST_FIRST(link) ((link)->next) ++#define DWC_LIST_LAST(link) ((link)->prev) ++#define DWC_LIST_END(link) (link) ++#define DWC_LIST_NEXT(link) ((link)->next) ++#define DWC_LIST_PREV(link) ((link)->prev) ++#define DWC_LIST_EMPTY(link) \ ++ (DWC_LIST_FIRST(link) == DWC_LIST_END(link)) ++#define DWC_LIST_ENTRY(link, type, field) (type *) \ ++ ((uint8_t *)(link) - (size_t)(&((type *)0)->field)) ++ ++#define DWC_LIST_INSERT_HEAD(list, link) do { \ ++ (link)->next = (list)->next; \ ++ (link)->prev = (list); \ ++ (list)->next->prev = link; \ ++ (list)->next = link; \ ++} while(0) ++ ++#define DWC_LIST_INSERT_TAIL(list, link) do { \ ++ (link)->next = list; \ ++ (link)->prev = (list)->prev; \ ++ (list)->prev->next = link; \ ++ (list)->prev = link; \ ++} while(0) ++ ++#define DWC_LIST_REMOVE(link) do { \ ++ (link)->next->prev = (link)->prev; \ ++ (link)->prev->next = (link)->next; \ ++} while(0) ++ ++#define DWC_LIST_REMOVE_INIT(link) do { \ ++ DWC_LIST_REMOVE(link); \ ++ DWC_LIST_INIT(link); \ ++} while(0) ++ ++#define DWC_LIST_MOVE_HEAD(list, link) do { \ ++ DWC_LIST_REMOVE(link); \ ++ DWC_LIST_INSERT_HEAD(list, link); \ ++} while(0) ++ ++#define DWC_LIST_MOVE_TAIL(list, link) do { \ ++ DWC_LIST_REMOVE(link); \ ++ DWC_LIST_INSERT_TAIL(list, link); \ ++} while(0) ++ ++#define DWC_LIST_FOREACH(var, list) \ ++ for((var) = DWC_LIST_FIRST(list); \ ++ (var) != DWC_LIST_END(list); \ ++ (var) = DWC_LIST_NEXT(var)) ++ ++#define DWC_LIST_FOREACH_SAFE(var, var2, list) \ ++ for((var) = DWC_LIST_FIRST(list), var2 = DWC_LIST_NEXT(var); \ ++ (var) != DWC_LIST_END(list); \ ++ (var) = (var2), var2 = DWC_LIST_NEXT(var2)) ++ ++#define DWC_LIST_FOREACH_REVERSE(var, list) \ ++ for((var) = DWC_LIST_LAST(list); \ ++ (var) != DWC_LIST_END(list); \ ++ (var) = DWC_LIST_PREV(var)) ++ ++/* ++ * Singly-linked List definitions. ++ */ ++#define DWC_SLIST_HEAD(name, type) \ ++struct name { \ ++ struct type *slh_first; /* first element */ \ ++} ++ ++#define DWC_SLIST_HEAD_INITIALIZER(head) \ ++ { NULL } ++ ++#define DWC_SLIST_ENTRY(type) \ ++struct { \ ++ struct type *sle_next; /* next element */ \ ++} ++ ++/* ++ * Singly-linked List access methods. ++ */ ++#define DWC_SLIST_FIRST(head) ((head)->slh_first) ++#define DWC_SLIST_END(head) NULL ++#define DWC_SLIST_EMPTY(head) (SLIST_FIRST(head) == SLIST_END(head)) ++#define DWC_SLIST_NEXT(elm, field) ((elm)->field.sle_next) ++ ++#define DWC_SLIST_FOREACH(var, head, field) \ ++ for((var) = SLIST_FIRST(head); \ ++ (var) != SLIST_END(head); \ ++ (var) = SLIST_NEXT(var, field)) ++ ++#define DWC_SLIST_FOREACH_PREVPTR(var, varp, head, field) \ ++ for ((varp) = &SLIST_FIRST((head)); \ ++ ((var) = *(varp)) != SLIST_END(head); \ ++ (varp) = &SLIST_NEXT((var), field)) ++ ++/* ++ * Singly-linked List functions. ++ */ ++#define DWC_SLIST_INIT(head) { \ ++ SLIST_FIRST(head) = SLIST_END(head); \ ++} ++ ++#define DWC_SLIST_INSERT_AFTER(slistelm, elm, field) do { \ ++ (elm)->field.sle_next = (slistelm)->field.sle_next; \ ++ (slistelm)->field.sle_next = (elm); \ ++} while (0) ++ ++#define DWC_SLIST_INSERT_HEAD(head, elm, field) do { \ ++ (elm)->field.sle_next = (head)->slh_first; \ ++ (head)->slh_first = (elm); \ ++} while (0) ++ ++#define DWC_SLIST_REMOVE_NEXT(head, elm, field) do { \ ++ (elm)->field.sle_next = (elm)->field.sle_next->field.sle_next; \ ++} while (0) ++ ++#define DWC_SLIST_REMOVE_HEAD(head, field) do { \ ++ (head)->slh_first = (head)->slh_first->field.sle_next; \ ++} while (0) ++ ++#define DWC_SLIST_REMOVE(head, elm, type, field) do { \ ++ if ((head)->slh_first == (elm)) { \ ++ SLIST_REMOVE_HEAD((head), field); \ ++ } \ ++ else { \ ++ struct type *curelm = (head)->slh_first; \ ++ while( curelm->field.sle_next != (elm) ) \ ++ curelm = curelm->field.sle_next; \ ++ curelm->field.sle_next = \ ++ curelm->field.sle_next->field.sle_next; \ ++ } \ ++} while (0) ++ ++#if 0 ++ ++/* ++ * List definitions. ++ */ ++#define DWC_LIST_HEAD(name, type) \ ++struct name { \ ++ struct type *lh_first; /* first element */ \ ++} ++ ++#define DWC_LIST_HEAD_INITIALIZER(head) \ ++ { NULL } ++ ++#define DWC_LIST_ENTRY(type) \ ++struct { \ ++ struct type *le_next; /* next element */ \ ++ struct type **le_prev; /* address of previous next element */ \ ++} ++ ++/* ++ * List access methods ++ */ ++#define DWC_LIST_FIRST(head) ((head)->lh_first) ++#define DWC_LIST_END(head) NULL ++#define DWC_LIST_EMPTY(head) (DWC_LIST_FIRST(head) == DWC_LIST_END(head)) ++#define DWC_LIST_NEXT(elm, field) ((elm)->field.le_next) ++ ++#define DWC_LIST_FOREACH(var, head, field) \ ++ for((var) = DWC_LIST_FIRST(head); \ ++ (var)!= DWC_LIST_END(head); \ ++ (var) = DWC_LIST_NEXT(var, field)) ++#define DWC_LIST_FOREACH_SAFE(var, var2, head, field) \ ++ for((var) = DWC_LIST_FIRST(head), var2 = DWC_LIST_NEXT(var, field); \ ++ (var) != DWC_LIST_END(head); \ ++ (var) = var2, var2 = DWC_LIST_NEXT(var, field)) ++ ++/* ++ * List functions. ++ */ ++#define DWC_LIST_INIT(head) do { \ ++ DWC_LIST_FIRST(head) = DWC_LIST_END(head); \ ++} while (0) ++ ++#define DWC_LIST_INSERT_AFTER(listelm, elm, field) do { \ ++ if (((elm)->field.le_next = (listelm)->field.le_next) != NULL) \ ++ (listelm)->field.le_next->field.le_prev = \ ++ &(elm)->field.le_next; \ ++ (listelm)->field.le_next = (elm); \ ++ (elm)->field.le_prev = &(listelm)->field.le_next; \ ++} while (0) ++ ++#define DWC_LIST_INSERT_BEFORE(listelm, elm, field) do { \ ++ (elm)->field.le_prev = (listelm)->field.le_prev; \ ++ (elm)->field.le_next = (listelm); \ ++ *(listelm)->field.le_prev = (elm); \ ++ (listelm)->field.le_prev = &(elm)->field.le_next; \ ++} while (0) ++ ++#define DWC_LIST_INSERT_HEAD(head, elm, field) do { \ ++ if (((elm)->field.le_next = (head)->lh_first) != NULL) \ ++ (head)->lh_first->field.le_prev = &(elm)->field.le_next;\ ++ (head)->lh_first = (elm); \ ++ (elm)->field.le_prev = &(head)->lh_first; \ ++} while (0) ++ ++#define DWC_LIST_REMOVE(elm, field) do { \ ++ if ((elm)->field.le_next != NULL) \ ++ (elm)->field.le_next->field.le_prev = \ ++ (elm)->field.le_prev; \ ++ *(elm)->field.le_prev = (elm)->field.le_next; \ ++} while (0) ++ ++#define DWC_LIST_REPLACE(elm, elm2, field) do { \ ++ if (((elm2)->field.le_next = (elm)->field.le_next) != NULL) \ ++ (elm2)->field.le_next->field.le_prev = \ ++ &(elm2)->field.le_next; \ ++ (elm2)->field.le_prev = (elm)->field.le_prev; \ ++ *(elm2)->field.le_prev = (elm2); \ ++} while (0) ++ ++#endif ++ ++/* ++ * Simple queue definitions. ++ */ ++#define DWC_SIMPLEQ_HEAD(name, type) \ ++struct name { \ ++ struct type *sqh_first; /* first element */ \ ++ struct type **sqh_last; /* addr of last next element */ \ ++} ++ ++#define DWC_SIMPLEQ_HEAD_INITIALIZER(head) \ ++ { NULL, &(head).sqh_first } ++ ++#define DWC_SIMPLEQ_ENTRY(type) \ ++struct { \ ++ struct type *sqe_next; /* next element */ \ ++} ++ ++/* ++ * Simple queue access methods. ++ */ ++#define DWC_SIMPLEQ_FIRST(head) ((head)->sqh_first) ++#define DWC_SIMPLEQ_END(head) NULL ++#define DWC_SIMPLEQ_EMPTY(head) (SIMPLEQ_FIRST(head) == SIMPLEQ_END(head)) ++#define DWC_SIMPLEQ_NEXT(elm, field) ((elm)->field.sqe_next) ++ ++#define DWC_SIMPLEQ_FOREACH(var, head, field) \ ++ for((var) = SIMPLEQ_FIRST(head); \ ++ (var) != SIMPLEQ_END(head); \ ++ (var) = SIMPLEQ_NEXT(var, field)) ++ ++/* ++ * Simple queue functions. ++ */ ++#define DWC_SIMPLEQ_INIT(head) do { \ ++ (head)->sqh_first = NULL; \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++} while (0) ++ ++#define DWC_SIMPLEQ_INSERT_HEAD(head, elm, field) do { \ ++ if (((elm)->field.sqe_next = (head)->sqh_first) == NULL) \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ (head)->sqh_first = (elm); \ ++} while (0) ++ ++#define DWC_SIMPLEQ_INSERT_TAIL(head, elm, field) do { \ ++ (elm)->field.sqe_next = NULL; \ ++ *(head)->sqh_last = (elm); \ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++} while (0) ++ ++#define DWC_SIMPLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ ++ if (((elm)->field.sqe_next = (listelm)->field.sqe_next) == NULL)\ ++ (head)->sqh_last = &(elm)->field.sqe_next; \ ++ (listelm)->field.sqe_next = (elm); \ ++} while (0) ++ ++#define DWC_SIMPLEQ_REMOVE_HEAD(head, field) do { \ ++ if (((head)->sqh_first = (head)->sqh_first->field.sqe_next) == NULL) \ ++ (head)->sqh_last = &(head)->sqh_first; \ ++} while (0) ++ ++/* ++ * Tail queue definitions. ++ */ ++#define DWC_TAILQ_HEAD(name, type) \ ++struct name { \ ++ struct type *tqh_first; /* first element */ \ ++ struct type **tqh_last; /* addr of last next element */ \ ++} ++ ++#define DWC_TAILQ_HEAD_INITIALIZER(head) \ ++ { NULL, &(head).tqh_first } ++ ++#define DWC_TAILQ_ENTRY(type) \ ++struct { \ ++ struct type *tqe_next; /* next element */ \ ++ struct type **tqe_prev; /* address of previous next element */ \ ++} ++ ++/* ++ * tail queue access methods ++ */ ++#define DWC_TAILQ_FIRST(head) ((head)->tqh_first) ++#define DWC_TAILQ_END(head) NULL ++#define DWC_TAILQ_NEXT(elm, field) ((elm)->field.tqe_next) ++#define DWC_TAILQ_LAST(head, headname) \ ++ (*(((struct headname *)((head)->tqh_last))->tqh_last)) ++/* XXX */ ++#define DWC_TAILQ_PREV(elm, headname, field) \ ++ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last)) ++#define DWC_TAILQ_EMPTY(head) \ ++ (TAILQ_FIRST(head) == TAILQ_END(head)) ++ ++#define DWC_TAILQ_FOREACH(var, head, field) \ ++ for((var) = TAILQ_FIRST(head); \ ++ (var) != TAILQ_END(head); \ ++ (var) = TAILQ_NEXT(var, field)) ++ ++#define DWC_TAILQ_FOREACH_REVERSE(var, head, headname, field) \ ++ for((var) = TAILQ_LAST(head, headname); \ ++ (var) != TAILQ_END(head); \ ++ (var) = TAILQ_PREV(var, headname, field)) ++ ++/* ++ * Tail queue functions. ++ */ ++#define DWC_TAILQ_INIT(head) do { \ ++ (head)->tqh_first = NULL; \ ++ (head)->tqh_last = &(head)->tqh_first; \ ++} while (0) ++ ++#define DWC_TAILQ_INSERT_HEAD(head, elm, field) do { \ ++ if (((elm)->field.tqe_next = (head)->tqh_first) != NULL) \ ++ (head)->tqh_first->field.tqe_prev = \ ++ &(elm)->field.tqe_next; \ ++ else \ ++ (head)->tqh_last = &(elm)->field.tqe_next; \ ++ (head)->tqh_first = (elm); \ ++ (elm)->field.tqe_prev = &(head)->tqh_first; \ ++} while (0) ++ ++#define DWC_TAILQ_INSERT_TAIL(head, elm, field) do { \ ++ (elm)->field.tqe_next = NULL; \ ++ (elm)->field.tqe_prev = (head)->tqh_last; \ ++ *(head)->tqh_last = (elm); \ ++ (head)->tqh_last = &(elm)->field.tqe_next; \ ++} while (0) ++ ++#define DWC_TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \ ++ if (((elm)->field.tqe_next = (listelm)->field.tqe_next) != NULL)\ ++ (elm)->field.tqe_next->field.tqe_prev = \ ++ &(elm)->field.tqe_next; \ ++ else \ ++ (head)->tqh_last = &(elm)->field.tqe_next; \ ++ (listelm)->field.tqe_next = (elm); \ ++ (elm)->field.tqe_prev = &(listelm)->field.tqe_next; \ ++} while (0) ++ ++#define DWC_TAILQ_INSERT_BEFORE(listelm, elm, field) do { \ ++ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \ ++ (elm)->field.tqe_next = (listelm); \ ++ *(listelm)->field.tqe_prev = (elm); \ ++ (listelm)->field.tqe_prev = &(elm)->field.tqe_next; \ ++} while (0) ++ ++#define DWC_TAILQ_REMOVE(head, elm, field) do { \ ++ if (((elm)->field.tqe_next) != NULL) \ ++ (elm)->field.tqe_next->field.tqe_prev = \ ++ (elm)->field.tqe_prev; \ ++ else \ ++ (head)->tqh_last = (elm)->field.tqe_prev; \ ++ *(elm)->field.tqe_prev = (elm)->field.tqe_next; \ ++} while (0) ++ ++#define DWC_TAILQ_REPLACE(head, elm, elm2, field) do { \ ++ if (((elm2)->field.tqe_next = (elm)->field.tqe_next) != NULL) \ ++ (elm2)->field.tqe_next->field.tqe_prev = \ ++ &(elm2)->field.tqe_next; \ ++ else \ ++ (head)->tqh_last = &(elm2)->field.tqe_next; \ ++ (elm2)->field.tqe_prev = (elm)->field.tqe_prev; \ ++ *(elm2)->field.tqe_prev = (elm2); \ ++} while (0) ++ ++/* ++ * Circular queue definitions. ++ */ ++#define DWC_CIRCLEQ_HEAD(name, type) \ ++struct name { \ ++ struct type *cqh_first; /* first element */ \ ++ struct type *cqh_last; /* last element */ \ ++} ++ ++#define DWC_CIRCLEQ_HEAD_INITIALIZER(head) \ ++ { DWC_CIRCLEQ_END(&head), DWC_CIRCLEQ_END(&head) } ++ ++#define DWC_CIRCLEQ_ENTRY(type) \ ++struct { \ ++ struct type *cqe_next; /* next element */ \ ++ struct type *cqe_prev; /* previous element */ \ ++} ++ ++/* ++ * Circular queue access methods ++ */ ++#define DWC_CIRCLEQ_FIRST(head) ((head)->cqh_first) ++#define DWC_CIRCLEQ_LAST(head) ((head)->cqh_last) ++#define DWC_CIRCLEQ_END(head) ((void *)(head)) ++#define DWC_CIRCLEQ_NEXT(elm, field) ((elm)->field.cqe_next) ++#define DWC_CIRCLEQ_PREV(elm, field) ((elm)->field.cqe_prev) ++#define DWC_CIRCLEQ_EMPTY(head) \ ++ (DWC_CIRCLEQ_FIRST(head) == DWC_CIRCLEQ_END(head)) ++ ++#define DWC_CIRCLEQ_EMPTY_ENTRY(elm, field) (((elm)->field.cqe_next == NULL) && ((elm)->field.cqe_prev == NULL)) ++ ++#define DWC_CIRCLEQ_FOREACH(var, head, field) \ ++ for((var) = DWC_CIRCLEQ_FIRST(head); \ ++ (var) != DWC_CIRCLEQ_END(head); \ ++ (var) = DWC_CIRCLEQ_NEXT(var, field)) ++ ++#define DWC_CIRCLEQ_FOREACH_SAFE(var, var2, head, field) \ ++ for((var) = DWC_CIRCLEQ_FIRST(head), var2 = DWC_CIRCLEQ_NEXT(var, field); \ ++ (var) != DWC_CIRCLEQ_END(head); \ ++ (var) = var2, var2 = DWC_CIRCLEQ_NEXT(var, field)) ++ ++#define DWC_CIRCLEQ_FOREACH_REVERSE(var, head, field) \ ++ for((var) = DWC_CIRCLEQ_LAST(head); \ ++ (var) != DWC_CIRCLEQ_END(head); \ ++ (var) = DWC_CIRCLEQ_PREV(var, field)) ++ ++/* ++ * Circular queue functions. ++ */ ++#define DWC_CIRCLEQ_INIT(head) do { \ ++ (head)->cqh_first = DWC_CIRCLEQ_END(head); \ ++ (head)->cqh_last = DWC_CIRCLEQ_END(head); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INIT_ENTRY(elm, field) do { \ ++ (elm)->field.cqe_next = NULL; \ ++ (elm)->field.cqe_prev = NULL; \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INSERT_AFTER(head, listelm, elm, field) do { \ ++ (elm)->field.cqe_next = (listelm)->field.cqe_next; \ ++ (elm)->field.cqe_prev = (listelm); \ ++ if ((listelm)->field.cqe_next == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_last = (elm); \ ++ else \ ++ (listelm)->field.cqe_next->field.cqe_prev = (elm); \ ++ (listelm)->field.cqe_next = (elm); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INSERT_BEFORE(head, listelm, elm, field) do { \ ++ (elm)->field.cqe_next = (listelm); \ ++ (elm)->field.cqe_prev = (listelm)->field.cqe_prev; \ ++ if ((listelm)->field.cqe_prev == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_first = (elm); \ ++ else \ ++ (listelm)->field.cqe_prev->field.cqe_next = (elm); \ ++ (listelm)->field.cqe_prev = (elm); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INSERT_HEAD(head, elm, field) do { \ ++ (elm)->field.cqe_next = (head)->cqh_first; \ ++ (elm)->field.cqe_prev = DWC_CIRCLEQ_END(head); \ ++ if ((head)->cqh_last == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_last = (elm); \ ++ else \ ++ (head)->cqh_first->field.cqe_prev = (elm); \ ++ (head)->cqh_first = (elm); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_INSERT_TAIL(head, elm, field) do { \ ++ (elm)->field.cqe_next = DWC_CIRCLEQ_END(head); \ ++ (elm)->field.cqe_prev = (head)->cqh_last; \ ++ if ((head)->cqh_first == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_first = (elm); \ ++ else \ ++ (head)->cqh_last->field.cqe_next = (elm); \ ++ (head)->cqh_last = (elm); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_REMOVE(head, elm, field) do { \ ++ if ((elm)->field.cqe_next == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_last = (elm)->field.cqe_prev; \ ++ else \ ++ (elm)->field.cqe_next->field.cqe_prev = \ ++ (elm)->field.cqe_prev; \ ++ if ((elm)->field.cqe_prev == DWC_CIRCLEQ_END(head)) \ ++ (head)->cqh_first = (elm)->field.cqe_next; \ ++ else \ ++ (elm)->field.cqe_prev->field.cqe_next = \ ++ (elm)->field.cqe_next; \ ++} while (0) ++ ++#define DWC_CIRCLEQ_REMOVE_INIT(head, elm, field) do { \ ++ DWC_CIRCLEQ_REMOVE(head, elm, field); \ ++ DWC_CIRCLEQ_INIT_ENTRY(elm, field); \ ++} while (0) ++ ++#define DWC_CIRCLEQ_REPLACE(head, elm, elm2, field) do { \ ++ if (((elm2)->field.cqe_next = (elm)->field.cqe_next) == \ ++ DWC_CIRCLEQ_END(head)) \ ++ (head).cqh_last = (elm2); \ ++ else \ ++ (elm2)->field.cqe_next->field.cqe_prev = (elm2); \ ++ if (((elm2)->field.cqe_prev = (elm)->field.cqe_prev) == \ ++ DWC_CIRCLEQ_END(head)) \ ++ (head).cqh_first = (elm2); \ ++ else \ ++ (elm2)->field.cqe_prev->field.cqe_next = (elm2); \ ++} while (0) ++ ++#endif /* !_SYS_QUEUE_H_ */ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_mem.c +@@ -0,0 +1,172 @@ ++#include "dwc_os.h" ++#include "dwc_list.h" ++ ++/* Memory Debugging */ ++#ifdef DEBUG_MEMORY ++ ++struct allocation ++{ ++ void *addr; ++ char *func; ++ int line; ++ uint32_t size; ++ int dma; ++ DWC_CIRCLEQ_ENTRY(allocation) entry; ++}; ++ ++DWC_CIRCLEQ_HEAD(allocation_queue, allocation); ++ ++struct allocation_manager ++{ ++ struct allocation_queue allocations; ++ ++ /* statistics */ ++ int num; ++ int num_freed; ++ int num_active; ++ uint32_t total; ++ uint32_t current; ++ uint32_t max; ++}; ++ ++ ++static struct allocation_manager *manager = NULL; ++ ++static void add_allocation(uint32_t size, char const* func, int line, void *addr, int dma) ++{ ++ struct allocation *a = __DWC_ALLOC_ATOMIC(sizeof(*a)); ++ a->func = __DWC_ALLOC_ATOMIC(DWC_STRLEN(func)+1); ++ DWC_MEMCPY(a->func, func, DWC_STRLEN(func)+1); ++ a->line = line; ++ a->size = size; ++ a->addr = addr; ++ a->dma = dma; ++ DWC_CIRCLEQ_INSERT_TAIL(&manager->allocations, a, entry); ++ ++ /* Update stats */ ++ manager->num ++; ++ manager->num_active ++; ++ manager->total += size; ++ manager->current += size; ++ if (manager->max < manager->current) { ++ manager->max = manager->current; ++ } ++} ++ ++static struct allocation *find_allocation(void *addr) ++{ ++ struct allocation *a; ++ DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) { ++ if (a->addr == addr) { ++ return a; ++ } ++ } ++ return NULL; ++} ++ ++static void free_allocation(void *addr, char const* func, int line) ++{ ++ struct allocation *a = find_allocation(addr); ++ if (!a && func && (line >= 0)) { ++ DWC_ASSERT(0, "Free of address %p that was never allocated or already freed %s:%d", addr, func, line); ++ return; ++ } ++ DWC_CIRCLEQ_REMOVE(&manager->allocations, a, entry); ++ ++ manager->num_active --; ++ manager->num_freed ++; ++ manager->current -= a->size; ++ __DWC_FREE(a->func); ++ __DWC_FREE(a); ++} ++ ++void dwc_memory_debug_start(void) ++{ ++ DWC_ASSERT(manager == NULL, "Memory debugging has already started\n"); ++ if (manager == NULL) { ++ manager = __DWC_ALLOC(sizeof(*manager)); ++ } ++ ++ DWC_CIRCLEQ_INIT(&manager->allocations); ++ manager->num = 0; ++ manager->num_freed = 0; ++ manager->num_active = 0; ++ manager->total = 0; ++ manager->current = 0; ++ manager->max = 0; ++} ++ ++void dwc_memory_debug_stop(void) ++{ ++ struct allocation *a; ++ dwc_memory_debug_report(); ++ ++ DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) { ++ DWC_ERROR("Memory leaked from %s:%d\n", a->func, a->line); ++ free_allocation(a->addr, NULL, -1); ++ } ++ ++ __DWC_FREE(manager); ++} ++ ++void dwc_memory_debug_report(void) ++{ ++ struct allocation *a; ++ DWC_PRINTF("\n\n\n----------------- Memory Debugging Report -----------------\n\n"); ++ DWC_PRINTF("Num Allocations = %d\n", manager->num); ++ DWC_PRINTF("Freed = %d\n", manager->num_freed); ++ DWC_PRINTF("Active = %d\n", manager->num_active); ++ DWC_PRINTF("Current Memory Used = %d\n", manager->current); ++ DWC_PRINTF("Total Memory Used = %d\n", manager->total); ++ DWC_PRINTF("Maximum Memory Used at Once = %d\n", manager->max); ++ DWC_PRINTF("Unfreed allocations:\n"); ++ ++ DWC_CIRCLEQ_FOREACH(a, &manager->allocations, entry) { ++ DWC_PRINTF(" addr=%p, size=%d from %s:%d, DMA=%d\n", a->addr, a->size, a->func, a->line, a->dma); ++ } ++} ++ ++ ++ ++/* The replacement functions */ ++void *dwc_alloc_debug(uint32_t size, char const* func, int line) ++{ ++ void *addr = __DWC_ALLOC(size); ++ add_allocation(size, func, line, addr, 0); ++ return addr; ++} ++ ++void *dwc_alloc_atomic_debug(uint32_t size, char const* func, int line) ++{ ++ void *addr = __DWC_ALLOC_ATOMIC(size); ++ add_allocation(size, func, line, addr, 0); ++ return addr; ++} ++ ++void dwc_free_debug(void *addr, char const* func, int line) ++{ ++ free_allocation(addr, func, line); ++ __DWC_FREE(addr); ++} ++ ++void *dwc_dma_alloc_debug(uint32_t size, dwc_dma_t *dma_addr, char const *func, int line) ++{ ++ void *addr = __DWC_DMA_ALLOC(size, dma_addr); ++ add_allocation(size, func, line, addr, 1); ++ return addr; ++} ++ ++void *dwc_dma_alloc_atomic_debug(uint32_t size, dwc_dma_t *dma_addr, char const *func, int line) ++{ ++ void *addr = __DWC_DMA_ALLOC_ATOMIC(size, dma_addr); ++ add_allocation(size, func, line, addr, 1); ++ return addr; ++} ++ ++void dwc_dma_free_debug(uint32_t size, void *virt_addr, dwc_dma_t dma_addr, char const *func, int line) ++{ ++ free_allocation(virt_addr, func, line); ++ __DWC_DMA_FREE(size, virt_addr, dma_addr); ++} ++ ++#endif /* DEBUG_MEMORY */ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_modpow.c +@@ -0,0 +1,622 @@ ++/* Bignum routines adapted from PUTTY sources. PuTTY copyright notice follows. ++ * ++ * PuTTY is copyright 1997-2007 Simon Tatham. ++ * ++ * Portions copyright Robert de Bath, Joris van Rantwijk, Delian ++ * Delchev, Andreas Schultz, Jeroen Massar, Wez Furlong, Nicolas Barry, ++ * Justin Bradford, Ben Harris, Malcolm Smith, Ahmad Khalifa, Markus ++ * Kuhn, and CORE SDI S.A. ++ * ++ * Permission is hereby granted, free of charge, to any person ++ * obtaining a copy of this software and associated documentation files ++ * (the "Software"), to deal in the Software without restriction, ++ * including without limitation the rights to use, copy, modify, merge, ++ * publish, distribute, sublicense, and/or sell copies of the Software, ++ * and to permit persons to whom the Software is furnished to do so, ++ * subject to the following conditions: ++ * ++ * The above copyright notice and this permission notice shall be ++ * included in all copies or substantial portions of the Software. ++ ++ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, ++ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF ++ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND ++ * NONINFRINGEMENT. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE ++ * FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF ++ * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION ++ * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ++ * ++ */ ++ ++#ifndef CONFIG_MACH_IPMATE ++ ++ ++#include "dwc_modpow.h" ++ ++#define BIGNUM_INT_MASK 0xFFFFFFFFUL ++#define BIGNUM_TOP_BIT 0x80000000UL ++#define BIGNUM_INT_BITS 32 ++ ++ ++static void *snmalloc(size_t n, size_t size) ++{ ++ void *p; ++ size *= n; ++ if (size == 0) size = 1; ++ p = DWC_ALLOC(size); ++ return p; ++} ++ ++#define snewn(n, type) ((type *)snmalloc((n), sizeof(type))) ++#define sfree DWC_FREE ++ ++/* ++ * Usage notes: ++ * * Do not call the DIVMOD_WORD macro with expressions such as array ++ * subscripts, as some implementations object to this (see below). ++ * * Note that none of the division methods below will cope if the ++ * quotient won't fit into BIGNUM_INT_BITS. Callers should be careful ++ * to avoid this case. ++ * If this condition occurs, in the case of the x86 DIV instruction, ++ * an overflow exception will occur, which (according to a correspondent) ++ * will manifest on Windows as something like ++ * 0xC0000095: Integer overflow ++ * The C variant won't give the right answer, either. ++ */ ++ ++#define MUL_WORD(w1, w2) ((BignumDblInt)w1 * w2) ++ ++#if defined __GNUC__ && defined __i386__ ++#define DIVMOD_WORD(q, r, hi, lo, w) \ ++ __asm__("div %2" : \ ++ "=d" (r), "=a" (q) : \ ++ "r" (w), "d" (hi), "a" (lo)) ++#else ++#define DIVMOD_WORD(q, r, hi, lo, w) do { \ ++ BignumDblInt n = (((BignumDblInt)hi) << BIGNUM_INT_BITS) | lo; \ ++ q = n / w; \ ++ r = n % w; \ ++} while (0) ++#endif ++ ++// q = n / w; ++// r = n % w; ++ ++#define BIGNUM_INT_BYTES (BIGNUM_INT_BITS / 8) ++ ++#define BIGNUM_INTERNAL ++ ++static Bignum newbn(int length) ++{ ++ Bignum b = snewn(length + 1, BignumInt); ++ //if (!b) ++ //abort(); /* FIXME */ ++ DWC_MEMSET(b, 0, (length + 1) * sizeof(*b)); ++ b[0] = length; ++ return b; ++} ++ ++void freebn(Bignum b) ++{ ++ /* ++ * Burn the evidence, just in case. ++ */ ++ DWC_MEMSET(b, 0, sizeof(b[0]) * (b[0] + 1)); ++ sfree(b); ++} ++ ++/* ++ * Compute c = a * b. ++ * Input is in the first len words of a and b. ++ * Result is returned in the first 2*len words of c. ++ */ ++static void internal_mul(BignumInt *a, BignumInt *b, ++ BignumInt *c, int len) ++{ ++ int i, j; ++ BignumDblInt t; ++ ++ for (j = 0; j < 2 * len; j++) ++ c[j] = 0; ++ ++ for (i = len - 1; i >= 0; i--) { ++ t = 0; ++ for (j = len - 1; j >= 0; j--) { ++ t += MUL_WORD(a[i], (BignumDblInt) b[j]); ++ t += (BignumDblInt) c[i + j + 1]; ++ c[i + j + 1] = (BignumInt) t; ++ t = t >> BIGNUM_INT_BITS; ++ } ++ c[i] = (BignumInt) t; ++ } ++} ++ ++static void internal_add_shifted(BignumInt *number, ++ unsigned n, int shift) ++{ ++ int word = 1 + (shift / BIGNUM_INT_BITS); ++ int bshift = shift % BIGNUM_INT_BITS; ++ BignumDblInt addend; ++ ++ addend = (BignumDblInt)n << bshift; ++ ++ while (addend) { ++ addend += number[word]; ++ number[word] = (BignumInt) addend & BIGNUM_INT_MASK; ++ addend >>= BIGNUM_INT_BITS; ++ word++; ++ } ++} ++ ++/* ++ * Compute a = a % m. ++ * Input in first alen words of a and first mlen words of m. ++ * Output in first alen words of a ++ * (of which first alen-mlen words will be zero). ++ * The MSW of m MUST have its high bit set. ++ * Quotient is accumulated in the `quotient' array, which is a Bignum ++ * rather than the internal bigendian format. Quotient parts are shifted ++ * left by `qshift' before adding into quot. ++ */ ++static void internal_mod(BignumInt *a, int alen, ++ BignumInt *m, int mlen, ++ BignumInt *quot, int qshift) ++{ ++ BignumInt m0, m1; ++ unsigned int h; ++ int i, k; ++ ++ m0 = m[0]; ++ if (mlen > 1) ++ m1 = m[1]; ++ else ++ m1 = 0; ++ ++ for (i = 0; i <= alen - mlen; i++) { ++ BignumDblInt t; ++ unsigned int q, r, c, ai1; ++ ++ if (i == 0) { ++ h = 0; ++ } else { ++ h = a[i - 1]; ++ a[i - 1] = 0; ++ } ++ ++ if (i == alen - 1) ++ ai1 = 0; ++ else ++ ai1 = a[i + 1]; ++ ++ /* Find q = h:a[i] / m0 */ ++ if (h >= m0) { ++ /* ++ * Special case. ++ * ++ * To illustrate it, suppose a BignumInt is 8 bits, and ++ * we are dividing (say) A1:23:45:67 by A1:B2:C3. Then ++ * our initial division will be 0xA123 / 0xA1, which ++ * will give a quotient of 0x100 and a divide overflow. ++ * However, the invariants in this division algorithm ++ * are not violated, since the full number A1:23:... is ++ * _less_ than the quotient prefix A1:B2:... and so the ++ * following correction loop would have sorted it out. ++ * ++ * In this situation we set q to be the largest ++ * quotient we _can_ stomach (0xFF, of course). ++ */ ++ q = BIGNUM_INT_MASK; ++ } else { ++ /* Macro doesn't want an array subscript expression passed ++ * into it (see definition), so use a temporary. */ ++ BignumInt tmplo = a[i]; ++ DIVMOD_WORD(q, r, h, tmplo, m0); ++ ++ /* Refine our estimate of q by looking at ++ h:a[i]:a[i+1] / m0:m1 */ ++ t = MUL_WORD(m1, q); ++ if (t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) { ++ q--; ++ t -= m1; ++ r = (r + m0) & BIGNUM_INT_MASK; /* overflow? */ ++ if (r >= (BignumDblInt) m0 && ++ t > ((BignumDblInt) r << BIGNUM_INT_BITS) + ai1) q--; ++ } ++ } ++ ++ /* Subtract q * m from a[i...] */ ++ c = 0; ++ for (k = mlen - 1; k >= 0; k--) { ++ t = MUL_WORD(q, m[k]); ++ t += c; ++ c = (unsigned)(t >> BIGNUM_INT_BITS); ++ if ((BignumInt) t > a[i + k]) ++ c++; ++ a[i + k] -= (BignumInt) t; ++ } ++ ++ /* Add back m in case of borrow */ ++ if (c != h) { ++ t = 0; ++ for (k = mlen - 1; k >= 0; k--) { ++ t += m[k]; ++ t += a[i + k]; ++ a[i + k] = (BignumInt) t; ++ t = t >> BIGNUM_INT_BITS; ++ } ++ q--; ++ } ++ if (quot) ++ internal_add_shifted(quot, q, qshift + BIGNUM_INT_BITS * (alen - mlen - i)); ++ } ++} ++ ++/* ++ * Compute p % mod. ++ * The most significant word of mod MUST be non-zero. ++ * We assume that the result array is the same size as the mod array. ++ * We optionally write out a quotient if `quotient' is non-NULL. ++ * We can avoid writing out the result if `result' is NULL. ++ */ ++void bigdivmod(Bignum p, Bignum mod, Bignum result, Bignum quotient) ++{ ++ BignumInt *n, *m; ++ int mshift; ++ int plen, mlen, i, j; ++ ++ /* Allocate m of size mlen, copy mod to m */ ++ /* We use big endian internally */ ++ mlen = mod[0]; ++ m = snewn(mlen, BignumInt); ++ for (j = 0; j < mlen; j++) ++ m[j] = mod[mod[0] - j]; ++ ++ /* Shift m left to make msb bit set */ ++ for (mshift = 0; mshift < BIGNUM_INT_BITS-1; mshift++) ++ if ((m[0] << mshift) & BIGNUM_TOP_BIT) ++ break; ++ if (mshift) { ++ for (i = 0; i < mlen - 1; i++) ++ m[i] = (m[i] << mshift) | (m[i + 1] >> (BIGNUM_INT_BITS - mshift)); ++ m[mlen - 1] = m[mlen - 1] << mshift; ++ } ++ ++ plen = p[0]; ++ /* Ensure plen > mlen */ ++ if (plen <= mlen) ++ plen = mlen + 1; ++ ++ /* Allocate n of size plen, copy p to n */ ++ n = snewn(plen, BignumInt); ++ for (j = 0; j < plen; j++) ++ n[j] = 0; ++ for (j = 1; j <= (int)p[0]; j++) ++ n[plen - j] = p[j]; ++ ++ /* Main computation */ ++ internal_mod(n, plen, m, mlen, quotient, mshift); ++ ++ /* Fixup result in case the modulus was shifted */ ++ if (mshift) { ++ for (i = plen - mlen - 1; i < plen - 1; i++) ++ n[i] = (n[i] << mshift) | (n[i + 1] >> (BIGNUM_INT_BITS - mshift)); ++ n[plen - 1] = n[plen - 1] << mshift; ++ internal_mod(n, plen, m, mlen, quotient, 0); ++ for (i = plen - 1; i >= plen - mlen; i--) ++ n[i] = (n[i] >> mshift) | (n[i - 1] << (BIGNUM_INT_BITS - mshift)); ++ } ++ ++ /* Copy result to buffer */ ++ if (result) { ++ for (i = 1; i <= (int)result[0]; i++) { ++ int j = plen - i; ++ result[i] = j >= 0 ? n[j] : 0; ++ } ++ } ++ ++ /* Free temporary arrays */ ++ for (i = 0; i < mlen; i++) ++ m[i] = 0; ++ sfree(m); ++ for (i = 0; i < plen; i++) ++ n[i] = 0; ++ sfree(n); ++} ++ ++/* ++ * Simple remainder. ++ */ ++Bignum bigmod(Bignum a, Bignum b) ++{ ++ Bignum r = newbn(b[0]); ++ bigdivmod(a, b, r, NULL); ++ return r; ++} ++ ++/* ++ * Compute (base ^ exp) % mod. ++ */ ++Bignum dwc_modpow(Bignum base_in, Bignum exp, Bignum mod) ++{ ++ BignumInt *a, *b, *n, *m; ++ int mshift; ++ int mlen, i, j; ++ Bignum base, result; ++ ++ /* ++ * The most significant word of mod needs to be non-zero. It ++ * should already be, but let's make sure. ++ */ ++ //assert(mod[mod[0]] != 0); ++ ++ /* ++ * Make sure the base is smaller than the modulus, by reducing ++ * it modulo the modulus if not. ++ */ ++ base = bigmod(base_in, mod); ++ ++ /* Allocate m of size mlen, copy mod to m */ ++ /* We use big endian internally */ ++ mlen = mod[0]; ++ m = snewn(mlen, BignumInt); ++ for (j = 0; j < mlen; j++) ++ m[j] = mod[mod[0] - j]; ++ ++ /* Shift m left to make msb bit set */ ++ for (mshift = 0; mshift < BIGNUM_INT_BITS - 1; mshift++) ++ if ((m[0] << mshift) & BIGNUM_TOP_BIT) ++ break; ++ if (mshift) { ++ for (i = 0; i < mlen - 1; i++) ++ m[i] = ++ (m[i] << mshift) | (m[i + 1] >> ++ (BIGNUM_INT_BITS - mshift)); ++ m[mlen - 1] = m[mlen - 1] << mshift; ++ } ++ ++ /* Allocate n of size mlen, copy base to n */ ++ n = snewn(mlen, BignumInt); ++ i = mlen - base[0]; ++ for (j = 0; j < i; j++) ++ n[j] = 0; ++ for (j = 0; j < base[0]; j++) ++ n[i + j] = base[base[0] - j]; ++ ++ /* Allocate a and b of size 2*mlen. Set a = 1 */ ++ a = snewn(2 * mlen, BignumInt); ++ b = snewn(2 * mlen, BignumInt); ++ for (i = 0; i < 2 * mlen; i++) ++ a[i] = 0; ++ a[2 * mlen - 1] = 1; ++ ++ /* Skip leading zero bits of exp. */ ++ i = 0; ++ j = BIGNUM_INT_BITS - 1; ++ while (i < exp[0] && (exp[exp[0] - i] & (1 << j)) == 0) { ++ j--; ++ if (j < 0) { ++ i++; ++ j = BIGNUM_INT_BITS - 1; ++ } ++ } ++ ++ /* Main computation */ ++ while (i < exp[0]) { ++ while (j >= 0) { ++ internal_mul(a + mlen, a + mlen, b, mlen); ++ internal_mod(b, mlen * 2, m, mlen, NULL, 0); ++ if ((exp[exp[0] - i] & (1 << j)) != 0) { ++ internal_mul(b + mlen, n, a, mlen); ++ internal_mod(a, mlen * 2, m, mlen, NULL, 0); ++ } else { ++ BignumInt *t; ++ t = a; ++ a = b; ++ b = t; ++ } ++ j--; ++ } ++ i++; ++ j = BIGNUM_INT_BITS - 1; ++ } ++ ++ /* Fixup result in case the modulus was shifted */ ++ if (mshift) { ++ for (i = mlen - 1; i < 2 * mlen - 1; i++) ++ a[i] = ++ (a[i] << mshift) | (a[i + 1] >> ++ (BIGNUM_INT_BITS - mshift)); ++ a[2 * mlen - 1] = a[2 * mlen - 1] << mshift; ++ internal_mod(a, mlen * 2, m, mlen, NULL, 0); ++ for (i = 2 * mlen - 1; i >= mlen; i--) ++ a[i] = ++ (a[i] >> mshift) | (a[i - 1] << ++ (BIGNUM_INT_BITS - mshift)); ++ } ++ ++ /* Copy result to buffer */ ++ result = newbn(mod[0]); ++ for (i = 0; i < mlen; i++) ++ result[result[0] - i] = a[i + mlen]; ++ while (result[0] > 1 && result[result[0]] == 0) ++ result[0]--; ++ ++ /* Free temporary arrays */ ++ for (i = 0; i < 2 * mlen; i++) ++ a[i] = 0; ++ sfree(a); ++ for (i = 0; i < 2 * mlen; i++) ++ b[i] = 0; ++ sfree(b); ++ for (i = 0; i < mlen; i++) ++ m[i] = 0; ++ sfree(m); ++ for (i = 0; i < mlen; i++) ++ n[i] = 0; ++ sfree(n); ++ ++ freebn(base); ++ ++ return result; ++} ++ ++ ++#ifdef UNITTEST ++ ++static __u32 dh_p[] = { ++ 96, ++ 0xFFFFFFFF, ++ 0xFFFFFFFF, ++ 0xA93AD2CA, ++ 0x4B82D120, ++ 0xE0FD108E, ++ 0x43DB5BFC, ++ 0x74E5AB31, ++ 0x08E24FA0, ++ 0xBAD946E2, ++ 0x770988C0, ++ 0x7A615D6C, ++ 0xBBE11757, ++ 0x177B200C, ++ 0x521F2B18, ++ 0x3EC86A64, ++ 0xD8760273, ++ 0xD98A0864, ++ 0xF12FFA06, ++ 0x1AD2EE6B, ++ 0xCEE3D226, ++ 0x4A25619D, ++ 0x1E8C94E0, ++ 0xDB0933D7, ++ 0xABF5AE8C, ++ 0xA6E1E4C7, ++ 0xB3970F85, ++ 0x5D060C7D, ++ 0x8AEA7157, ++ 0x58DBEF0A, ++ 0xECFB8504, ++ 0xDF1CBA64, ++ 0xA85521AB, ++ 0x04507A33, ++ 0xAD33170D, ++ 0x8AAAC42D, ++ 0x15728E5A, ++ 0x98FA0510, ++ 0x15D22618, ++ 0xEA956AE5, ++ 0x3995497C, ++ 0x95581718, ++ 0xDE2BCBF6, ++ 0x6F4C52C9, ++ 0xB5C55DF0, ++ 0xEC07A28F, ++ 0x9B2783A2, ++ 0x180E8603, ++ 0xE39E772C, ++ 0x2E36CE3B, ++ 0x32905E46, ++ 0xCA18217C, ++ 0xF1746C08, ++ 0x4ABC9804, ++ 0x670C354E, ++ 0x7096966D, ++ 0x9ED52907, ++ 0x208552BB, ++ 0x1C62F356, ++ 0xDCA3AD96, ++ 0x83655D23, ++ 0xFD24CF5F, ++ 0x69163FA8, ++ 0x1C55D39A, ++ 0x98DA4836, ++ 0xA163BF05, ++ 0xC2007CB8, ++ 0xECE45B3D, ++ 0x49286651, ++ 0x7C4B1FE6, ++ 0xAE9F2411, ++ 0x5A899FA5, ++ 0xEE386BFB, ++ 0xF406B7ED, ++ 0x0BFF5CB6, ++ 0xA637ED6B, ++ 0xF44C42E9, ++ 0x625E7EC6, ++ 0xE485B576, ++ 0x6D51C245, ++ 0x4FE1356D, ++ 0xF25F1437, ++ 0x302B0A6D, ++ 0xCD3A431B, ++ 0xEF9519B3, ++ 0x8E3404DD, ++ 0x514A0879, ++ 0x3B139B22, ++ 0x020BBEA6, ++ 0x8A67CC74, ++ 0x29024E08, ++ 0x80DC1CD1, ++ 0xC4C6628B, ++ 0x2168C234, ++ 0xC90FDAA2, ++ 0xFFFFFFFF, ++ 0xFFFFFFFF, ++}; ++ ++static __u32 dh_a[] = { ++ 8, ++ 0xdf367516, ++ 0x86459caa, ++ 0xe2d459a4, ++ 0xd910dae0, ++ 0x8a8b5e37, ++ 0x67ab31c6, ++ 0xf0b55ea9, ++ 0x440051d6, ++}; ++ ++static __u32 dh_b[] = { ++ 8, ++ 0xded92656, ++ 0xe07a048a, ++ 0x6fa452cd, ++ 0x2df89d30, ++ 0xc75f1b0f, ++ 0x8ce3578f, ++ 0x7980a324, ++ 0x5daec786, ++}; ++ ++static __u32 dh_g[] = { ++ 1, ++ 2, ++}; ++ ++int main(void) ++{ ++ int i; ++ __u32 *k; ++ k = modpow(dh_g, dh_a, dh_p); ++ ++ printf("\n\n"); ++ for (i=0; i<k[0]; i++) { ++ __u32 word32 = k[k[0] - i]; ++ __u16 l = word32 & 0xffff; ++ __u16 m = (word32 & 0xffff0000) >> 16; ++ printf("%04x %04x ", m, l); ++ if (!((i + 1)%13)) printf("\n"); ++ } ++ printf("\n\n"); ++ ++ if ((k[0] == 0x60) && (k[1] == 0x28e490e5) && (k[0x60] == 0x5a0d3d4e)) { ++ printf("PASS\n\n"); ++ } ++ else { ++ printf("FAIL\n\n"); ++ } ++ ++} ++ ++#endif /* UNITTEST */ ++ ++#endif /* CONFIG_MACH_IPMATE */ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_modpow.h +@@ -0,0 +1,26 @@ ++/* ++ * dwc_modpow.h ++ * See dwc_modpow.c for license and changes ++ */ ++#ifndef _DWC_MODPOW_H ++#define _DWC_MODPOW_H ++ ++#include "dwc_os.h" ++ ++/** @file ++ * ++ * This file defines the module exponentiation function which is only used ++ * internally by the DWC UWB modules for calculation of PKs during numeric ++ * association. The routine is taken from the PUTTY, an open source terminal ++ * emulator. The PUTTY License is preserved in the dwc_modpow.c file. ++ * ++ */ ++ ++typedef uint32_t BignumInt; ++typedef uint64_t BignumDblInt; ++typedef BignumInt *Bignum; ++ ++/* Compute modular exponentiaion */ ++extern Bignum dwc_modpow(Bignum base_in, Bignum exp, Bignum mod); ++ ++#endif /* _LINUX_BIGNUM_H */ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_notifier.c +@@ -0,0 +1,256 @@ ++#include "dwc_notifier.h" ++#include "dwc_list.h" ++ ++typedef struct dwc_observer ++{ ++ void *observer; ++ dwc_notifier_callback_t callback; ++ void *data; ++ char *notification; ++ DWC_CIRCLEQ_ENTRY(dwc_observer) list_entry; ++} observer_t; ++ ++DWC_CIRCLEQ_HEAD(observer_queue, dwc_observer); ++ ++typedef struct dwc_notifier ++{ ++ void *object; ++ struct observer_queue observers; ++ DWC_CIRCLEQ_ENTRY(dwc_notifier) list_entry; ++} notifier_t; ++ ++DWC_CIRCLEQ_HEAD(notifier_queue, dwc_notifier); ++ ++typedef struct manager ++{ ++ dwc_workq_t *wq; ++ dwc_mutex_t *mutex; ++ struct notifier_queue notifiers; ++} manager_t; ++ ++static manager_t *manager = NULL; ++ ++static void create_manager(void) ++{ ++ manager = DWC_ALLOC(sizeof(manager_t)); ++ DWC_CIRCLEQ_INIT(&manager->notifiers); ++ manager->wq = DWC_WORKQ_ALLOC("DWC Notification WorkQ"); ++} ++ ++static void free_manager(void) ++{ ++ DWC_WORKQ_FREE(manager->wq); ++ /* All notifiers must have unregistered themselves before this module ++ * can be removed. Hitting this assertion indicates a programmer ++ * error. */ ++ DWC_ASSERT(DWC_CIRCLEQ_EMPTY(&manager->notifiers), "Notification manager being freed before all notifiers have been removed"); ++ DWC_FREE(manager); ++} ++ ++#ifdef DEBUG ++static void dump_manager(void) ++{ ++ notifier_t *n; ++ observer_t *o; ++ DWC_ASSERT(manager, "Notification manager not found"); ++ DWC_DEBUG("List of all notifiers and observers:"); ++ DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) { ++ DWC_DEBUG("Notifier %p has observers:", n->object); ++ DWC_CIRCLEQ_FOREACH(o, &n->observers, list_entry) { ++ DWC_DEBUG(" %p watching %s", o->observer, o->notification); ++ } ++ } ++} ++#else ++#define dump_manager(...) ++#endif ++ ++static observer_t *alloc_observer(void *observer, char *notification, dwc_notifier_callback_t callback, void *data) ++{ ++ observer_t *new_observer = DWC_ALLOC(sizeof(observer_t)); ++ DWC_CIRCLEQ_INIT_ENTRY(new_observer, list_entry); ++ new_observer->observer = observer; ++ new_observer->notification = notification; ++ new_observer->callback = callback; ++ new_observer->data = data; ++ return new_observer; ++} ++ ++static void free_observer(observer_t *observer) ++{ ++ DWC_FREE(observer); ++} ++ ++static notifier_t *alloc_notifier(void *object) ++{ ++ notifier_t *notifier; ++ ++ if (!object) { ++ return NULL; ++ } ++ ++ notifier = DWC_ALLOC(sizeof(notifier_t)); ++ DWC_CIRCLEQ_INIT(¬ifier->observers); ++ DWC_CIRCLEQ_INIT_ENTRY(notifier, list_entry); ++ ++ notifier->object = object; ++ return notifier; ++} ++ ++static void free_notifier(notifier_t *notifier) ++{ ++ observer_t *observer; ++ DWC_CIRCLEQ_FOREACH(observer, ¬ifier->observers, list_entry) { ++ free_observer(observer); ++ } ++ DWC_FREE(notifier); ++} ++ ++static notifier_t *find_notifier(void *object) ++{ ++ notifier_t *notifier; ++ if (!object) { ++ return NULL; ++ } ++ DWC_ASSERT(manager, "Notification manager not found"); ++ DWC_CIRCLEQ_FOREACH(notifier, &manager->notifiers, list_entry) { ++ if (notifier->object == object) { ++ return notifier; ++ } ++ } ++ return NULL; ++} ++ ++void dwc_alloc_notification_manager(void) ++{ ++ create_manager(); ++} ++ ++void dwc_free_notification_manager(void) ++{ ++ free_manager(); ++} ++ ++dwc_notifier_t *dwc_register_notifier(void *object) ++{ ++ notifier_t *notifier = find_notifier(object); ++ DWC_ASSERT(manager, "Notification manager not found"); ++ if (notifier) { ++ DWC_ERROR("Notifier %p is already registered", object); ++ return NULL; ++ } ++ ++ notifier = alloc_notifier(object); ++ DWC_CIRCLEQ_INSERT_TAIL(&manager->notifiers, notifier, list_entry); ++ ++ ++ DWC_INFO("Notifier %p registered", object); ++ dump_manager(); ++ ++ return notifier; ++} ++ ++void dwc_unregister_notifier(dwc_notifier_t *notifier) ++{ ++ DWC_ASSERT(manager, "Notification manager not found"); ++ if (!DWC_CIRCLEQ_EMPTY(¬ifier->observers)) { ++ observer_t *o; ++ DWC_ERROR("Notifier %p has active observers when removing", notifier->object); ++ DWC_CIRCLEQ_FOREACH(o, ¬ifier->observers, list_entry) { ++ DWC_DEBUG(" %p watching %s", o->observer, o->notification); ++ } ++ DWC_ASSERT(DWC_CIRCLEQ_EMPTY(¬ifier->observers), "Notifier %p has active observers when removing", notifier); ++ } ++ ++ DWC_CIRCLEQ_REMOVE_INIT(&manager->notifiers, notifier, list_entry); ++ free_notifier(notifier); ++ ++ DWC_INFO("Notifier unregistered"); ++ dump_manager(); ++} ++ ++/* Add an observer to observe the notifier for a particular state, event, or notification. */ ++int dwc_add_observer(void *observer, void *object, char *notification, dwc_notifier_callback_t callback, void *data) ++{ ++ notifier_t *notifier = find_notifier(object); ++ observer_t *new_observer; ++ if (!notifier) { ++ DWC_ERROR("Notifier %p is not found when adding observer", object); ++ return -1; ++ } ++ ++ new_observer = alloc_observer(observer, notification, callback, data); ++ ++ DWC_CIRCLEQ_INSERT_TAIL(¬ifier->observers, new_observer, list_entry); ++ ++ DWC_INFO("Added observer %p to notifier %p observing notification %s, callback=%p, data=%p", ++ observer, object, notification, callback, data); ++ ++ dump_manager(); ++ return 0; ++} ++ ++int dwc_remove_observer(void *observer) ++{ ++ notifier_t *n; ++ DWC_ASSERT(manager, "Notification manager not found"); ++ DWC_CIRCLEQ_FOREACH(n, &manager->notifiers, list_entry) { ++ observer_t *o; ++ observer_t *o2; ++ DWC_CIRCLEQ_FOREACH_SAFE(o, o2, &n->observers, list_entry) { ++ if (o->observer == observer) { ++ DWC_CIRCLEQ_REMOVE_INIT(&n->observers, o, list_entry); ++ DWC_INFO("Removing observer %p from notifier %p watching notification %s:", ++ o->observer, n->object, o->notification); ++ free_observer(o); ++ } ++ } ++ } ++ ++ dump_manager(); ++ return 0; ++} ++ ++typedef struct callback_data ++{ ++ dwc_notifier_callback_t cb; ++ void *observer; ++ void *data; ++ void *object; ++ void *notification; ++ void *notification_data; ++} cb_data_t; ++ ++static void cb_task(void *data) ++{ ++ cb_data_t *cb = (cb_data_t *)data; ++ cb->cb(cb->object, cb->notification, cb->observer, cb->notification_data, cb->data); ++ DWC_FREE(cb); ++} ++ ++void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data) ++{ ++ observer_t *o; ++ DWC_ASSERT(manager, "Notification manager not found"); ++ DWC_CIRCLEQ_FOREACH(o, ¬ifier->observers, list_entry) { ++ int len = DWC_STRLEN(notification); ++ if (DWC_STRLEN(o->notification) != len) { ++ continue; ++ } ++ ++ if (DWC_STRNCMP(o->notification, notification, len) == 0) { ++ cb_data_t *cb_data = DWC_ALLOC(sizeof(cb_data_t)); ++ cb_data->cb = o->callback; ++ cb_data->observer = o->observer; ++ cb_data->data = o->data; ++ cb_data->object = notifier->object; ++ cb_data->notification = notification; ++ cb_data->notification_data = notification_data; ++ DWC_DEBUG("Observer found %p for notification %s", o->observer, notification); ++ DWC_WORKQ_SCHEDULE(manager->wq, cb_task, cb_data, ++ "Notify callback from %p for Notification %s, to observer %p", ++ cb_data->object, notification, cb_data->observer); ++ } ++ } ++} ++ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_notifier.h +@@ -0,0 +1,112 @@ ++ ++#ifndef __DWC_NOTIFIER_H__ ++#define __DWC_NOTIFIER_H__ ++ ++#include "dwc_os.h" ++ ++/** @file ++ * ++ * A simple implementation of the Observer pattern. Any "module" can ++ * register as an observer or notifier. The notion of "module" is abstract and ++ * can mean anything used to identify either an observer or notifier. Usually ++ * it will be a pointer to a data structure which contains some state, ie an ++ * object. ++ * ++ * Before any notifiers can be added, the global notification manager must be ++ * brought up with dwc_alloc_notification_manager(). ++ * dwc_free_notification_manager() will bring it down and free all resources. ++ * These would typically be called upon module load and unload. The ++ * notification manager is a single global instance that handles all registered ++ * observable modules and observers so this should be done only once. ++ * ++ * A module can be observable by using Notifications to publicize some general ++ * information about it's state or operation. It does not care who listens, or ++ * even if anyone listens, or what they do with the information. The observable ++ * modules do not need to know any information about it's observers or their ++ * interface, or their state or data. ++ * ++ * Any module can register to emit Notifications. It should publish a list of ++ * notifications that it can emit and their behavior, such as when they will get ++ * triggered, and what information will be provided to the observer. Then it ++ * should register itself as an observable module. See dwc_register_notifier(). ++ * ++ * Any module can observe any observable, registered module, provided it has a ++ * handle to the other module and knows what notifications to observe. See ++ * dwc_add_observer(). ++ * ++ * A function of type dwc_notifier_callback_t is called whenever a notification ++ * is triggered with one or more observers observing it. This function is ++ * called in it's own process so it may sleep or block if needed. It is ++ * guaranteed to be called sometime after the notification has occurred and will ++ * be called once per each time the notification is triggered. It will NOT be ++ * called in the same process context used to trigger the notification. ++ * ++ * @section Limitiations ++ * ++ * Keep in mind that Notifications that can be triggered in rapid sucession may ++ * schedule too many processes too handle. Be aware of this limitation when ++ * designing to use notifications, and only add notifications for appropriate ++ * observable information. ++ * ++ * Also Notification callbacks are not synchronous. If you need to synchronize ++ * the behavior between module/observer you must use other means. And perhaps ++ * that will mean Notifications are not the proper solution. ++ */ ++ ++struct dwc_notifier; ++typedef struct dwc_notifier dwc_notifier_t; ++ ++/** The callback function must be of this type. ++ * ++ * @param object This is the object that is being observed. ++ * @param notification This is the notification that was triggered. ++ * @param observer This is the observer ++ * @param notification_data This is notification-specific data that the notifier ++ * has included in this notification. The value of this should be published in ++ * the documentation of the observable module with the notifications. ++ * @param user_data This is any custom data that the observer provided when ++ * adding itself as an observer to the notification. */ ++typedef void (*dwc_notifier_callback_t)(void *object, char *notification, void *observer, void *notification_data, void *user_data); ++ ++/** Brings up the notification manager. */ ++extern void dwc_alloc_notification_manager(void); ++/** Brings down the notification manager. */ ++extern void dwc_free_notification_manager(void); ++ ++/** This function register an observable module. A dwc_notifier_t object is ++ * returned to the observable module. This is an opaque object that is used by ++ * the observable module to trigger notifications. This object should only be ++ * accessible to functions that are authorized to trigger notifications for this ++ * module. Observers do not need this object. */ ++extern dwc_notifier_t *dwc_register_notifier(void *object); ++ ++/** This function unregister an observable module. All observers have to be ++ * removed prior to unregistration. */ ++extern void dwc_unregister_notifier(dwc_notifier_t *notifier); ++ ++/** Add a module as an observer to the observable module. The observable module ++ * needs to have previously registered with the notification manager. ++ * ++ * @param observer The observer module ++ * @param object The module to observe ++ * @param notification The notification to observe ++ * @param callback The callback function to call ++ * @param user_data Any additional user data to pass into the callback function */ ++extern int dwc_add_observer(void *observer, void *object, char *notification, dwc_notifier_callback_t callback, void *user_data); ++ ++/** Removes the specified observer from all notifications that it is currently ++ * observing. */ ++extern int dwc_remove_observer(void *observer); ++ ++/** This function triggers a Notification. It should be called by the ++ * observable module, or any module or library which the observable module ++ * allows to trigger notification on it's behalf. Such as the dwc_cc_t. ++ * ++ * dwc_notify is a non-blocking function. Callbacks are scheduled called in ++ * their own process context for each trigger. Callbacks can be blocking. ++ * dwc_notify can be called from interrupt context if needed. ++ * ++ */ ++void dwc_notify(dwc_notifier_t *notifier, char *notification, void *notification_data); ++ ++#endif /* __DWC_NOTIFIER_H__ */ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/dwc_os.h +@@ -0,0 +1,924 @@ ++/* ========================================================================= ++ * $File: //dwh/usb_iip/dev/software/dwc_common_port/dwc_os.h $ ++ * $Revision: #2 $ ++ * $Date: 2009/04/02 $ ++ * $Change: 1224130 $ ++ * ++ * Synopsys Portability Library Software and documentation ++ * (hereinafter, "Software") is an Unsupported proprietary work of ++ * Synopsys, Inc. unless otherwise expressly agreed to in writing ++ * between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product ++ * under any End User Software License Agreement or Agreement for ++ * Licensed Product with Synopsys or any supplement thereto. You are ++ * permitted to use and redistribute this Software in source and binary ++ * forms, with or without modification, provided that redistributions ++ * of source code must retain this notice. You may not view, use, ++ * disclose, copy or distribute this file or any information contained ++ * herein except pursuant to this license grant from Synopsys. If you ++ * do not agree with this notice, including the disclaimer below, then ++ * you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" ++ * BASIS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ++ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS ++ * FOR A PARTICULAR PURPOSE ARE HEREBY DISCLAIMED. IN NO EVENT SHALL ++ * SYNOPSYS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY ++ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT ++ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ++ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================= */ ++#ifndef _DWC_OS_H_ ++#define _DWC_OS_H_ ++ ++/** @file ++ * ++ * DWC portability library, low level os-wrapper functions ++ * ++ */ ++ ++/* These basic types need to be defined by some OS header file or custom header ++ * file for your specific target architecture. ++ * ++ * uint8_t, int8_t, uint16_t, int16_t, uint32_t, int32_t, uint64_t, int64_t ++ * ++ * Any custom or alternate header file must be added and enabled here. ++ */ ++ ++#ifdef DWC_LINUX ++# include <linux/types.h> ++# ifdef CONFIG_DEBUG_MUTEXES ++# include <linux/mutex.h> ++# endif ++#else ++# include <stdint.h> ++#endif ++ ++ ++/** @name Primitive Types and Values */ ++ ++/** We define a boolean type for consistency. Can be either YES or NO */ ++typedef uint8_t dwc_bool_t; ++#define YES 1 ++#define NO 0 ++ ++/** @todo make them positive and return the negative error code */ ++/** @name Error Codes */ ++#define DWC_E_INVALID 1001 ++#define DWC_E_NO_MEMORY 1002 ++#define DWC_E_NO_DEVICE 1003 ++#define DWC_E_NOT_SUPPORTED 1004 ++#define DWC_E_TIMEOUT 1005 ++#define DWC_E_BUSY 1006 ++#define DWC_E_AGAIN 1007 ++#define DWC_E_RESTART 1008 ++#define DWC_E_ABORT 1009 ++#define DWC_E_SHUTDOWN 1010 ++#define DWC_E_NO_DATA 1011 ++#define DWC_E_DISCONNECT 2000 ++#define DWC_E_UNKNOWN 3000 ++#define DWC_E_NO_STREAM_RES 4001 ++#define DWC_E_COMMUNICATION 4002 ++#define DWC_E_OVERFLOW 4003 ++#define DWC_E_PROTOCOL 4004 ++#define DWC_E_IN_PROGRESS 4005 ++#define DWC_E_PIPE 4006 ++#define DWC_E_IO 4007 ++#define DWC_E_NO_SPACE 4008 ++ ++/** @name Tracing/Logging Functions ++ * ++ * These function provide the capability to add tracing, debugging, and error ++ * messages, as well exceptions as assertions. The WUDEV uses these ++ * extensively. These could be logged to the main console, the serial port, an ++ * internal buffer, etc. These functions could also be no-op if they are too ++ * expensive on your system. By default undefining the DEBUG macro already ++ * no-ops some of these functions. */ ++ ++#include <stdarg.h> ++ ++/** Returns non-zero if in interrupt context. */ ++extern dwc_bool_t DWC_IN_IRQ(void); ++#define dwc_in_irq DWC_IN_IRQ ++ ++/** Returns "IRQ" if DWC_IN_IRQ is true. */ ++static inline char *dwc_irq(void) { ++ return DWC_IN_IRQ() ? "IRQ" : ""; ++} ++ ++/** ++ * A vprintf() clone. Just call vprintf if you've got it. ++ */ ++extern void DWC_VPRINTF(char *format, va_list args); ++#define dwc_vprintf DWC_VPRINTF ++ ++/** ++ * A vsnprintf() clone. Just call vprintf if you've got it. ++ */ ++extern int DWC_VSNPRINTF(char *str, int size, char *format, va_list args); ++#define dwc_vsnprintf DWC_VSNPRINTF ++ ++/** ++ * printf() clone. Just call printf if you've go it. ++ */ ++extern void DWC_PRINTF(char *format, ...) ++/* This provides compiler level static checking of the parameters if you're ++ * using GCC. */ ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 1, 2))); ++#else ++ ; ++#endif ++#define dwc_printf DWC_PRINTF ++ ++/** ++ * sprintf() clone. Just call sprintf if you've got it. ++ */ ++extern int DWC_SPRINTF(char *string, char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 2, 3))); ++#else ++ ; ++#endif ++#define dwc_sprintf DWC_SPRINTF ++ ++/** ++ * snprintf() clone. Just call snprintf if you've got it. ++ */ ++extern int DWC_SNPRINTF(char *string, int size, char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 3, 4))); ++#else ++ ; ++#endif ++#define dwc_snprintf DWC_SNPRINTF ++ ++/** ++ * Prints a WARNING message. On systems that don't differentiate between ++ * warnings and regular log messages, just print it. Indicates that something ++ * may be wrong with the driver. Works like printf(). ++ * ++ * Use the DWC_WARN macro to call this function. ++ */ ++extern void __DWC_WARN(char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 1, 2))); ++#else ++ ; ++#endif ++ ++/** ++ * Prints an error message. On systems that don't differentiate between errors ++ * and regular log messages, just print it. Indicates that something went wrong ++ * with the driver, but it can be recovered from. Works like printf(). ++ * ++ * Use the DWC_ERROR macro to call this function. ++ */ ++extern void __DWC_ERROR(char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 1, 2))); ++#else ++ ; ++#endif ++ ++/** ++ * Prints an exception error message and takes some user-defined action such as ++ * print out a backtrace or trigger a breakpoint. Indicates that something went ++ * abnormally wrong with the driver such as programmer error, or other ++ * exceptional condition. It should not be ignored so even on systems without ++ * printing capability, some action should be taken to notify the developer of ++ * it. Works like printf(). ++ */ ++extern void DWC_EXCEPTION(char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 1, 2))); ++#else ++ ; ++#endif ++#define dwc_exception DWC_EXCEPTION ++ ++#ifdef DEBUG ++/** ++ * Prints out a debug message. Used for logging/trace messages. ++ * ++ * Use the DWC_DEBUG macro to call this function ++ */ ++extern void __DWC_DEBUG(char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 1, 2))); ++#else ++ ; ++#endif ++#else ++#define __DWC_DEBUG printk ++#endif ++ ++/** ++ * Prints out a Debug message. ++ */ ++#define DWC_DEBUG(_format, _args...) __DWC_DEBUG("DEBUG:%s:%s: " _format "\n", __func__, dwc_irq(), ## _args) ++#define dwc_debug DWC_DEBUG ++/** ++ * Prints out an informative message. ++ */ ++#define DWC_INFO(_format, _args...) DWC_PRINTF("INFO:%s: " _format "\n", dwc_irq(), ## _args) ++#define dwc_info DWC_INFO ++/** ++ * Prints out a warning message. ++ */ ++#define DWC_WARN(_format, _args...) __DWC_WARN("WARN:%s:%s:%d: " _format "\n", dwc_irq(), __func__, __LINE__, ## _args) ++#define dwc_warn DWC_WARN ++/** ++ * Prints out an error message. ++ */ ++#define DWC_ERROR(_format, _args...) __DWC_ERROR("ERROR:%s:%s:%d: " _format "\n", dwc_irq(), __func__, __LINE__, ## _args) ++#define dwc_error DWC_ERROR ++ ++#define DWC_PROTO_ERROR(_format, _args...) __DWC_WARN("ERROR:%s:%s:%d: " _format "\n", dwc_irq(), __func__, __LINE__, ## _args) ++#define dwc_proto_error DWC_PROTO_ERROR ++ ++#ifdef DEBUG ++/** Prints out a exception error message if the _expr expression fails. Disabled ++ * if DEBUG is not enabled. */ ++#define DWC_ASSERT(_expr, _format, _args...) if (!(_expr)) { DWC_EXCEPTION("%s:%s:%d: " _format "\n", dwc_irq(), __FILE__, __LINE__, ## _args); } ++#else ++#define DWC_ASSERT(_x...) ++#endif ++#define dwc_assert DWC_ASSERT ++ ++/** @name Byter Ordering ++ * The following functions are for conversions between processor's byte ordering ++ * and specific ordering you want. ++ */ ++ ++/** Converts 32 bit data in CPU byte ordering to little endian. */ ++extern uint32_t DWC_CPU_TO_LE32(void *p); ++#define dwc_cpu_to_le32 DWC_CPU_TO_LE32 ++/** Converts 32 bit data in CPU byte orderint to big endian. */ ++extern uint32_t DWC_CPU_TO_BE32(void *p); ++#define dwc_cpu_to_be32 DWC_CPU_TO_BE32 ++ ++/** Converts 32 bit little endian data to CPU byte ordering. */ ++extern uint32_t DWC_LE32_TO_CPU(void *p); ++#define dwc_le32_to_cpu DWC_LE32_TO_CPU ++/** Converts 32 bit big endian data to CPU byte ordering. */ ++extern uint32_t DWC_BE32_TO_CPU(void *p); ++#define dwc_be32_to_cpu DWC_BE32_TO_CPU ++ ++/** Converts 16 bit data in CPU byte ordering to little endian. */ ++extern uint16_t DWC_CPU_TO_LE16(void *p); ++#define dwc_cpu_to_le16 DWC_CPU_TO_LE16 ++/** Converts 16 bit data in CPU byte orderint to big endian. */ ++extern uint16_t DWC_CPU_TO_BE16(void *p); ++#define dwc_cpu_to_be16 DWC_CPU_TO_BE16 ++ ++/** Converts 16 bit little endian data to CPU byte ordering. */ ++extern uint16_t DWC_LE16_TO_CPU(void *p); ++#define dwc_le16_to_cpu DWC_LE16_TO_CPU ++/** Converts 16 bit bi endian data to CPU byte ordering. */ ++extern uint16_t DWC_BE16_TO_CPU(void *p); ++#define dwc_be16_to_cpu DWC_BE16_TO_CPU ++ ++/** @name Register Read/Write ++ * ++ * The following five functions should be implemented to read/write registers of ++ * 32-bit and 64-bit sizes. All modules use this to read/write register values. ++ * The reg value is a pointer to the register calculated from the void *base ++ * variable passed into the driver when it is started. */ ++ ++/** Reads the content of a 32-bit register. */ ++extern uint32_t DWC_READ_REG32(uint32_t volatile *reg); ++#define dwc_read_reg32 DWC_READ_REG32 ++/** Reads the content of a 64-bit register. */ ++extern uint64_t DWC_READ_REG64(uint64_t volatile *reg); ++#define dwc_read_reg64 DWC_READ_REG64 ++/** Writes to a 32-bit register. */ ++extern void DWC_WRITE_REG32(uint32_t volatile *reg, uint32_t value); ++#define dwc_write_reg32 DWC_WRITE_REG32 ++/** Writes to a 64-bit register. */ ++extern void DWC_WRITE_REG64(uint64_t volatile *reg, uint64_t value); ++#define dwc_write_reg64 DWC_WRITE_REG64 ++/** ++ * Modify bit values in a register. Using the ++ * algorithm: (reg_contents & ~clear_mask) | set_mask. ++ */ ++extern void DWC_MODIFY_REG32(uint32_t volatile *reg, uint32_t clear_mask, uint32_t set_mask); ++#define dwc_modify_reg32 DWC_MODIFY_REG32 ++ ++/** @cond */ ++ ++/** @name Some convenience MACROS used internally. Define DEBUG_REGS to log the ++ * register writes. */ ++ ++#ifdef DEBUG_REGS ++ ++#define dwc_define_read_write_reg_n(_reg,_container_type) \ ++static inline uint32_t dwc_read_##_reg##_n(_container_type *container, int num) { \ ++ return DWC_READ_REG32(&container->regs->_reg[num]); \ ++} \ ++static inline void dwc_write_##_reg##_n(_container_type *container, int num, uint32_t data) { \ ++ DWC_DEBUG("WRITING %8s[%d]: %p: %08x", #_reg, num, &(((uint32_t*)container->regs->_reg)[num]), data); \ ++ DWC_WRITE_REG32(&(((uint32_t*)container->regs->_reg)[num]), data); \ ++} ++ ++#define dwc_define_read_write_reg(_reg,_container_type) \ ++static inline uint32_t dwc_read_##_reg(_container_type *container) { \ ++ return DWC_READ_REG32(&container->regs->_reg); \ ++} \ ++static inline void dwc_write_##_reg(_container_type *container, uint32_t data) { \ ++ DWC_DEBUG("WRITING %11s: %p: %08x", #_reg, &container->regs->_reg, data); \ ++ DWC_WRITE_REG32(&container->regs->_reg, data); \ ++} ++ ++#else ++ ++#define dwc_define_read_write_reg_n(_reg,_container_type) \ ++static inline uint32_t dwc_read_##_reg##_n(_container_type *container, int num) { \ ++ return DWC_READ_REG32(&container->regs->_reg[num]); \ ++} \ ++static inline void dwc_write_##_reg##_n(_container_type *container, int num, uint32_t data) { \ ++ DWC_WRITE_REG32(&(((uint32_t*)container->regs->_reg)[num]), data); \ ++} ++ ++#define dwc_define_read_write_reg(_reg,_container_type) \ ++static inline uint32_t dwc_read_##_reg(_container_type *container) { \ ++ return DWC_READ_REG32(&container->regs->_reg); \ ++} \ ++static inline void dwc_write_##_reg(_container_type *container, uint32_t data) { \ ++ DWC_WRITE_REG32(&container->regs->_reg, data); \ ++} ++ ++#endif ++ ++/** @endcond */ ++ ++ ++/** @name Crypto Functions ++ * ++ * These are the low-level cryptographic functions used by the driver. */ ++ ++/** Perform AES CBC */ ++extern int DWC_AES_CBC(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t iv[16], uint8_t *out); ++#define dwc_aes_cbc DWC_AES_CBC ++/** Fill the provided buffer with random bytes. These should be cryptographic grade random numbers. */ ++extern void DWC_RANDOM_BYTES(uint8_t *buffer, uint32_t length); ++#define dwc_random_bytes DWC_RANDOM_BYTES ++/** Perform the SHA-256 hash function */ ++extern int DWC_SHA256(uint8_t *message, uint32_t len, uint8_t *out); ++#define dwc_sha256 DWC_SHA256 ++/** Calculated the HMAC-SHA256 */ ++extern int DWC_HMAC_SHA256(uint8_t *message, uint32_t messagelen, uint8_t *key, uint32_t keylen, uint8_t *out); ++#define dwc_hmac_sha256 DWC_HMAC_SHA256 ++ ++ ++/** @name Memory Allocation ++ * ++ * These function provide access to memory allocation. There are only 2 DMA ++ * functions and 3 Regular memory functions that need to be implemented. None ++ * of the memory debugging routines need to be implemented. The allocation ++ * routines all ZERO the contents of the memory. ++ * ++ * Defining DEBUG_MEMORY turns on memory debugging and statistic gathering. ++ * This checks for memory leaks, keeping track of alloc/free pairs. It also ++ * keeps track of how much memory the driver is using at any given time. */ ++ ++#define DWC_PAGE_SIZE 4096 ++#define DWC_PAGE_OFFSET(addr) (((uint32_t)addr) & 0xfff) ++#define DWC_PAGE_ALIGNED(addr) ((((uint32_t)addr) & 0xfff) == 0) ++ ++#define DWC_INVALID_DMA_ADDR 0x0 ++ ++typedef uint32_t dwc_dma_t; ++ ++/** @todo these functions will be added in the future */ ++#if 0 ++/** ++ * Creates a DMA pool from which you can allocate DMA buffers. Buffers ++ * allocated from this pool will be guaranteed to meet the size, alignment, and ++ * boundary requirements specified. ++ * ++ * @param[in] size Specifies the size of the buffers that will be allocated from ++ * this pool. ++ * @param[in] align Specifies the byte alignment requirements of the buffers ++ * allocated from this pool. Must be a power of 2. ++ * @param[in] boundary Specifies the N-byte boundary that buffers allocated from ++ * this pool must not cross. ++ * ++ * @returns A pointer to an internal opaque structure which is not to be ++ * accessed outside of these library functions. Use this handle to specify ++ * which pools to allocate/free DMA buffers from and also to destroy the pool, ++ * when you are done with it. ++ */ ++extern dwc_pool_t *DWC_DMA_POOL_CREATE(uint32_t size, uint32_t align, uint32_t boundary); ++/** ++ * Destroy a DMA pool. All buffers allocated from that pool must be freed first. ++ */ ++extern void DWC_DMA_POOL_DESTROY(dwc_pool_t *pool); ++/** ++ * Allocate a buffer from the specified DMA pool and zeros its contents. ++ */ ++extern void *DWC_DMA_POOL_ALLOC(dwc_pool_t *pool, uint64_t *dma_addr); ++/** ++ * Free a previously allocated buffer from the DMA pool. ++ */ ++extern void DWC_DMA_POOL_FREE(dwc_pool_t *pool, void *vaddr, void *daddr); ++#endif ++ ++ ++/** Allocates a DMA capable buffer and zeroes its contents. */ ++extern void *__DWC_DMA_ALLOC(uint32_t size, dwc_dma_t *dma_addr); ++ ++/** Allocates a DMA capable buffer and zeroes its contents in atomic contest */ ++extern void *__DWC_DMA_ALLOC_ATOMIC(uint32_t size, dwc_dma_t *dma_addr); ++ ++/** Frees a previosly allocated buffer. */ ++extern void __DWC_DMA_FREE(uint32_t size, void *virt_addr, dwc_dma_t dma_addr); ++ ++/** Allocates a block of memory and zeroes its contents. */ ++extern void *__DWC_ALLOC(uint32_t size); ++ ++/** Allocates a block of memory and zeroes its contents, in an atomic manner ++ * which can be used inside interrupt context. The size should be sufficiently ++ * small, a few KB at most, such that failures are not likely to occur. Can just call ++ * __DWC_ALLOC if it is atomic. */ ++extern void *__DWC_ALLOC_ATOMIC(uint32_t size); ++ ++/** Frees a previously allocated buffer. */ ++extern void __DWC_FREE(void *addr); ++ ++#ifndef DEBUG_MEMORY ++ ++#define DWC_ALLOC(_size_) __DWC_ALLOC(_size_) ++#define DWC_ALLOC_ATOMIC(_size_) __DWC_ALLOC_ATOMIC(_size_) ++#define DWC_FREE(_addr_) __DWC_FREE(_addr_) ++#define DWC_DMA_ALLOC(_size_,_dma_) __DWC_DMA_ALLOC(_size_,_dma_) ++#define DWC_DMA_ALLOC_ATOMIC(_size_,_dma_) __DWC_DMA_ALLOC_ATOMIC(_size_,_dma_) ++#define DWC_DMA_FREE(_size_,_virt_,_dma_) __DWC_DMA_FREE(_size_,_virt_,_dma_) ++ ++#else ++ ++extern void *dwc_alloc_debug(uint32_t size, char const *func, int line); ++extern void *dwc_alloc_atomic_debug(uint32_t size, char const *func, int line); ++extern void dwc_free_debug(void *addr, char const *func, int line); ++extern void *dwc_dma_alloc_debug(uint32_t size, dwc_dma_t *dma_addr, char const *func, int line); ++extern void *dwc_dma_alloc_atomic_debug(uint32_t size, dwc_dma_t *dma_addr, char const *func, int line); ++extern void dwc_dma_free_debug(uint32_t size, void *virt_addr, dwc_dma_t dma_addr, char const *func, int line); ++ ++extern void dwc_memory_debug_start(void); ++extern void dwc_memory_debug_stop(void); ++extern void dwc_memory_debug_report(void); ++ ++#define DWC_ALLOC(_size_) (dwc_alloc_debug(_size_, __func__, __LINE__)) ++#define DWC_ALLOC_ATOMIC(_size_) (dwc_alloc_atomic_debug(_size_, __func__, __LINE__)) ++#define DWC_FREE(_addr_) (dwc_free_debug(_addr_, __func__, __LINE__)) ++#define DWC_DMA_ALLOC(_size_,_dma_) dwc_dma_alloc_debug(_size_, _dma_, __func__, __LINE__) ++#define DWC_DMA_ALLOC_ATOMIC(_size_,_dma_) dwc_dma_alloc_atomic_debug(_size_, _dma_, __func__, __LINE__) ++#define DWC_DMA_FREE(_size_,_virt_,_dma_) dwc_dma_free_debug(_size_, _virt_, _dma_, __func__, __LINE__) ++ ++#endif /* DEBUG_MEMORY */ ++ ++#define dwc_alloc DWC_ALLOC ++#define dwc_alloc_atomic DWC_ALLOC_ATOMIC ++#define dwc_free DWC_FREE ++#define dwc_dma_alloc DWC_DMA_ALLOC ++#define dwc_dma_alloc_atomic DWC_DMA_ALLOC_ATOMIC ++#define dwc_dma_free DWC_DMA_FREE ++ ++ ++/** @name Memory and String Processing */ ++ ++/** memset() clone */ ++extern void *DWC_MEMSET(void *dest, uint8_t byte, uint32_t size); ++#define dwc_memset DWC_MEMSET ++/** memcpy() clone */ ++extern void *DWC_MEMCPY(void *dest, void const *src, uint32_t size); ++#define dwc_memcpy DWC_MEMCPY ++/** memmove() clone */ ++extern void *DWC_MEMMOVE(void *dest, void *src, uint32_t size); ++#define dwc_memmove DWC_MEMMOVE ++/** memcmp() clone */ ++extern int DWC_MEMCMP(void *m1, void *m2, uint32_t size); ++#define dwc_memcmp DWC_MEMCMP ++/** strcmp() clone */ ++extern int DWC_STRCMP(void *s1, void *s2); ++#define dwc_strcmp DWC_STRCMP ++/** strncmp() clone */ ++extern int DWC_STRNCMP(void *s1, void *s2, uint32_t size); ++#define dwc_strncmp DWC_STRNCMP ++/** strlen() clone, for NULL terminated ASCII strings */ ++extern int DWC_STRLEN(char const *str); ++#define dwc_strlen DWC_STRLEN ++/** strcpy() clone, for NULL terminated ASCII strings */ ++extern char *DWC_STRCPY(char *to, const char *from); ++#define dwc_strcpy DWC_STRCPY ++ ++/** strdup() clone. If you wish to use memory allocation debugging, this ++ * implementation of strdup should use the DWC_* memory routines instead of ++ * calling a predefined strdup. Otherwise the memory allocated by this routine ++ * will not be seen by the debugging routines. */ ++extern char *DWC_STRDUP(char const *str); ++#define dwc_strdup DWC_STRDUP ++ ++/** NOT an atoi() clone. Read the description carefully. Returns an integer ++ * converted from the string str in base 10 unless the string begins with a "0x" ++ * in which case it is base 16. String must be a NULL terminated sequence of ++ * ASCII characters and may optionally begin with whitespace, a + or -, and a ++ * "0x" prefix if base 16. The remaining characters must be valid digits for ++ * the number and end with a NULL character. If any invalid characters are ++ * encountered or it returns with a negative error code and the results of the ++ * conversion are undefined. On sucess it returns 0. Overflow conditions are ++ * undefined. An example implementation using atoi() can be referenced from the ++ * Linux implementation. */ ++extern int DWC_ATOI(char *str, int32_t *value); ++#define dwc_atoi DWC_ATOI ++/** Same as above but for unsigned. */ ++extern int DWC_ATOUI(char *str, uint32_t *value); ++#define dwc_atoui DWC_ATOUI ++/** This routine returns a UTF16LE unicode encoded string from a UTF8 string. */ ++extern int DWC_UTF8_TO_UTF16LE(uint8_t const *utf8string, uint16_t *utf16string, unsigned len); ++#define dwc_utf8_to_utf16le DWC_UTF8_TO_UTF16LE ++ ++/** @name Wait queues ++ * ++ * Wait queues provide a means of synchronizing between threads or processes. A ++ * process can block on a waitq if some condition is not true, waiting for it to ++ * become true. When the waitq is triggered all waiting process will get ++ * unblocked and the condition will be check again. Waitqs should be triggered ++ * every time a condition can potentially change.*/ ++struct dwc_waitq; ++typedef struct dwc_waitq dwc_waitq_t; ++ ++/** The type of waitq condition callback function. This is called every time ++ * condition is evaluated. */ ++typedef int (*dwc_waitq_condition_t)(void *data); ++ ++/** Allocate a waitq */ ++extern dwc_waitq_t *DWC_WAITQ_ALLOC(void); ++#define dwc_waitq_alloc DWC_WAITQ_ALLOC ++/** Free a waitq */ ++extern void DWC_WAITQ_FREE(dwc_waitq_t *wq); ++#define dwc_waitq_free DWC_WAITQ_FREE ++ ++/** Check the condition and if it is false, block on the waitq. When unblocked, check the ++ * condition again. The function returns when the condition becomes true. The return value ++ * is 0 on condition true, DWC_WAITQ_ABORTED on abort or killed, or DWC_WAITQ_UNKNOWN on error. */ ++extern int32_t DWC_WAITQ_WAIT(dwc_waitq_t *wq, dwc_waitq_condition_t condition, void *data); ++#define dwc_waitq_wait DWC_WAITQ_WAIT; ++/** Check the condition and if it is false, block on the waitq. When unblocked, ++ * check the condition again. The function returns when the condition become ++ * true or the timeout has passed. The return value is 0 on condition true or ++ * DWC_TIMED_OUT on timeout, or DWC_WAITQ_ABORTED, or DWC_WAITQ_UNKNOWN on ++ * error. */ ++extern int32_t DWC_WAITQ_WAIT_TIMEOUT(dwc_waitq_t *wq, dwc_waitq_condition_t condition, void *data, int32_t msecs); ++#define dwc_waitq_wait_timeout DWC_WAITQ_WAIT_TIMEOUT ++/** Trigger a waitq, unblocking all processes. This should be called whenever a condition ++ * has potentially changed. */ ++extern void DWC_WAITQ_TRIGGER(dwc_waitq_t *wq); ++#define dwc_waitq_trigger DWC_WAITQ_TRIGGER ++/** Unblock all processes waiting on the waitq with an ABORTED result. */ ++extern void DWC_WAITQ_ABORT(dwc_waitq_t *wq); ++#define dwc_waitq_abort DWC_WAITQ_ABORT ++ ++/** @name Threads ++ * ++ * A thread must be explicitly stopped. It must check DWC_THREAD_SHOULD_STOP ++ * whenever it is woken up, and then return. The DWC_THREAD_STOP function ++ * returns the value from the thread. ++ */ ++ ++struct dwc_thread; ++typedef struct dwc_thread dwc_thread_t; ++ ++/** The thread function */ ++typedef int (*dwc_thread_function_t)(void *data); ++ ++/** Create a thread and start it running the thread_function. Returns a handle ++ * to the thread */ ++extern dwc_thread_t *DWC_THREAD_RUN(dwc_thread_function_t thread_function, char *name, void *data); ++#define dwc_thread_run DWC_THREAD_RUN ++/** Stops a thread. Return the value returned by the thread. Or will return ++ * DWC_ABORT if the thread never started. */ ++extern int DWC_THREAD_STOP(dwc_thread_t *thread); ++#define dwc_thread_stop DWC_THREAD_STOP ++/** Signifies to the thread that it must stop. */ ++extern dwc_bool_t DWC_THREAD_SHOULD_STOP(void); ++#define dwc_thread_should_stop DWC_THREAD_SHOULD_STOP ++ ++/** @name Work queues ++ * ++ * Workqs are used to queue a callback function to be called at some later time, ++ * in another thread. */ ++struct dwc_workq; ++typedef struct dwc_workq dwc_workq_t; ++ ++/** The type of the callback function to be called. */ ++typedef void (*dwc_work_callback_t)(void *data); ++ ++/** Allocate a workq */ ++extern dwc_workq_t *DWC_WORKQ_ALLOC(char *name); ++#define dwc_workq_alloc DWC_WORKQ_ALLOC ++/** Free a workq. All work must be completed before being freed. */ ++extern void DWC_WORKQ_FREE(dwc_workq_t *workq); ++#define dwc_workq_free DWC_WORKQ_FREE ++/** Schedule a callback on the workq, passing in data. The function will be ++ * scheduled at some later time. */ ++extern void DWC_WORKQ_SCHEDULE(dwc_workq_t *workq, dwc_work_callback_t work_cb, void *data, char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 4, 5))); ++#else ++ ; ++#endif ++#define dwc_workq_schedule DWC_WORKQ_SCHEDULE ++ ++/** Schedule a callback on the workq, that will be called until at least ++ * given number miliseconds have passed. */ ++extern void DWC_WORKQ_SCHEDULE_DELAYED(dwc_workq_t *workq, dwc_work_callback_t work_cb, void *data, uint32_t time, char *format, ...) ++#ifdef __GNUC__ ++ __attribute__ ((format(printf, 5, 6))); ++#else ++ ; ++#endif ++#define dwc_workq_schedule_delayed DWC_WORKQ_SCHEDULE_DELAYED ++ ++/** The number of processes in the workq */ ++extern int DWC_WORKQ_PENDING(dwc_workq_t *workq); ++#define dwc_workq_pending DWC_WORKQ_PENDING ++/** Blocks until all the work in the workq is complete or timed out. Returns < ++ * 0 on timeout. */ ++extern int DWC_WORKQ_WAIT_WORK_DONE(dwc_workq_t *workq, int timeout); ++#define dwc_workq_wait_work_done DWC_WORKQ_WAIT_WORK_DONE ++ ++ ++/** @name Tasklets ++ * ++ */ ++struct dwc_tasklet; ++typedef struct dwc_tasklet dwc_tasklet_t; ++ ++typedef void (*dwc_tasklet_callback_t)(void *data); ++ ++extern dwc_tasklet_t *DWC_TASK_ALLOC(dwc_tasklet_callback_t cb, void *data); ++#define dwc_task_alloc DWC_TASK_ALLOC ++extern void DWC_TASK_FREE(dwc_tasklet_t *t); ++#define dwc_task_free DWC_TASK_FREE ++extern void DWC_TASK_SCHEDULE(dwc_tasklet_t *task); ++#define dwc_task_schedule DWC_TASK_SCHEDULE ++ ++ ++/** @name Timer ++ * ++ * Callbacks must be small and atomic. ++ */ ++struct dwc_timer; ++typedef struct dwc_timer dwc_timer_t; ++ ++typedef void (*dwc_timer_callback_t)(void *data); ++ ++extern dwc_timer_t *DWC_TIMER_ALLOC(char *name, dwc_timer_callback_t cb, void *data); ++#define dwc_timer_alloc DWC_TIMER_ALLOC ++extern void DWC_TIMER_FREE(dwc_timer_t *timer); ++#define dwc_timer_free DWC_TIMER_FREE ++ ++/** Schedules the timer to run at time ms from now. And will repeat at every ++ * repeat_interval msec therafter ++ * ++ * Modifies a timer that is still awaiting execution to a new expiration time. ++ * The mod_time is added to the old time. */ ++extern void DWC_TIMER_SCHEDULE(dwc_timer_t *timer, uint32_t time); ++#define dwc_timer_schedule DWC_TIMER_SCHEDULE ++ ++/** Disables the timer from execution. */ ++extern void DWC_TIMER_CANCEL(dwc_timer_t *timer); ++#define dwc_timer_cancel DWC_TIMER_CANCEL ++ ++ ++ ++/** @name Spinlocks ++ * ++ * These locks are used when the work between the lock/unlock is atomic and ++ * short. Interrupts are also disabled during the lock/unlock and thus they are ++ * suitable to lock between interrupt/non-interrupt context. They also lock ++ * between processes if you have multiple CPUs or Preemption. If you don't have ++ * multiple CPUS or Preemption, then the you can simply implement the ++ * DWC_SPINLOCK and DWC_SPINUNLOCK to disable and enable interrupts. Because ++ * the work between the lock/unlock is atomic, the process context will never ++ * change, and so you never have to lock between processes. */ ++ ++struct dwc_spinlock; ++typedef struct dwc_spinlock dwc_spinlock_t; ++ ++/** Returns an initialized lock variable. This function should allocate and ++ * initialize the OS-specific data structure used for locking. This data ++ * structure is to be used for the DWC_LOCK and DWC_UNLOCK functions and should ++ * be freed by the DWC_FREE_LOCK when it is no longer used. */ ++extern dwc_spinlock_t *DWC_SPINLOCK_ALLOC(void); ++#define dwc_spinlock_alloc DWC_SPINLOCK_ALLOC ++ ++/** Frees an initialized lock variable. */ ++extern void DWC_SPINLOCK_FREE(dwc_spinlock_t *lock); ++#define dwc_spinlock_free DWC_SPINLOCK_FREE ++ ++/** Disables interrupts and blocks until it acquires the lock. ++ * ++ * @param lock Pointer to the spinlock. ++ * @param flags Unsigned long for irq flags storage. ++ */ ++extern void DWC_SPINLOCK_IRQSAVE(dwc_spinlock_t *lock, uint64_t *flags); ++#define dwc_spinlock_irqsave DWC_SPINLOCK_IRQSAVE ++ ++/** Re-enables the interrupt and releases the lock. ++ * ++ * @param lock Pointer to the spinlock. ++ * @param flags Unsigned long for irq flags storage. Must be the same as was ++ * passed into DWC_LOCK. ++ */ ++extern void DWC_SPINUNLOCK_IRQRESTORE(dwc_spinlock_t *lock, uint64_t flags); ++#define dwc_spinunlock_irqrestore DWC_SPINUNLOCK_IRQRESTORE ++ ++/** Blocks until it acquires the lock. ++ * ++ * @param lock Pointer to the spinlock. ++ */ ++extern void DWC_SPINLOCK(dwc_spinlock_t *lock); ++#define dwc_spinlock DWC_SPINLOCK ++ ++/** Releases the lock. ++ * ++ * @param lock Pointer to the spinlock. ++ */ ++extern void DWC_SPINUNLOCK(dwc_spinlock_t *lock); ++#define dwc_spinunlock DWC_SPINUNLOCK ++ ++/** @name Mutexes ++ * ++ * Unlike spinlocks Mutexes lock only between processes and the work between the ++ * lock/unlock CAN block, therefore it CANNOT be called from interrupt context. ++ */ ++ ++struct dwc_mutex; ++typedef struct dwc_mutex dwc_mutex_t; ++ ++ ++/* For Linux Mutex Debugging make it inline because the debugging routines use ++ * the symbol to determine recursive locking. This makes it falsely think ++ * recursive locking occurs. */ ++#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)) ++#define DWC_MUTEX_ALLOC_LINUX_DEBUG(__mutexp) ({ \ ++ __mutexp = (dwc_mutex_t *)DWC_ALLOC(sizeof(struct mutex)); \ ++ mutex_init((struct mutex *)__mutexp); \ ++}) ++#endif ++extern dwc_mutex_t *DWC_MUTEX_ALLOC(void); ++#define dwc_mutex_alloc DWC_MUTEX_ALLOC ++ ++/* For memory leak debugging when using Linux Mutex Debugging */ ++#if (defined(DWC_LINUX) && defined(CONFIG_DEBUG_MUTEXES)) ++#define DWC_MUTEX_FREE(__mutexp) do { \ ++ mutex_destroy((struct mutex *)__mutexp); \ ++ DWC_FREE(__mutexp); \ ++} while(0) ++#else ++extern void DWC_MUTEX_FREE(dwc_mutex_t *mutex); ++#define dwc_mutex_free DWC_MUTEX_FREE ++#endif ++ ++extern void DWC_MUTEX_LOCK(dwc_mutex_t *mutex); ++#define dwc_mutex_lock DWC_MUTEX_LOCK ++/** Non-blocking lock returns 1 on successful lock. */ ++extern int DWC_MUTEX_TRYLOCK(dwc_mutex_t *mutex); ++#define dwc_mutex_trylock DWC_MUTEX_TRYLOCK ++extern void DWC_MUTEX_UNLOCK(dwc_mutex_t *mutex); ++#define dwc_mutex_unlock DWC_MUTEX_UNLOCK ++ ++ ++ ++ ++/** @name Time */ ++ ++/** Microsecond delay. ++ * ++ * @param usecs Microseconds to delay. ++ */ ++extern void DWC_UDELAY(uint32_t usecs); ++#define dwc_udelay DWC_UDELAY ++ ++/** Millisecond delay. ++ * ++ * @param msecs Milliseconds to delay. ++ */ ++extern void DWC_MDELAY(uint32_t msecs); ++#define dwc_mdelay DWC_MDELAY ++ ++/** Non-busy waiting. ++ * Sleeps for specified number of milliseconds. ++ * ++ * @param msecs Milliseconds to sleep. ++ */ ++extern void DWC_MSLEEP(uint32_t msecs); ++#define dwc_msleep DWC_MSLEEP ++ ++extern uint32_t DWC_TIME(void); ++#define dwc_time DWC_TIME ++ ++#endif // _DWC_OS_H_ ++ ++ ++ ++ ++/** @mainpage DWC Portability and Common Library ++ * ++ * This is the documentation for the DWC Portability and Common Library. ++ * ++ * @section intro Introduction ++ * ++ * The DWC Portability library consists of wrapper calls and data structures to ++ * all low-level functions which are typically provided by the OS. The WUDEV ++ * driver uses only these functions. In order to port the WUDEV driver, only ++ * the functions in this library need to be re-implemented, with the same ++ * behavior as documented here. ++ * ++ * The Common library consists of higher level functions, which rely only on ++ * calling the functions from the DWC Portability library. These common ++ * routines are shared across modules. Some of the common libraries need to be ++ * used directly by the driver programmer when porting WUDEV. Such as the ++ * parameter and notification libraries. ++ * ++ * @section low Portability Library OS Wrapper Functions ++ * ++ * Any function starting with DWC and in all CAPS is a low-level OS-wrapper that ++ * needs to be implemented when porting, for example DWC_MUTEX_ALLOC(). All of ++ * these functions are included in the dwc_os.h file. ++ * ++ * There are many functions here covering a wide array of OS services. Please ++ * see dwc_os.h for details, and implementation notes for each function. ++ * ++ * @section common Common Library Functions ++ * ++ * Any function starting with dwc and in all lowercase is a common library ++ * routine. These functions have a portable implementation and do not need to ++ * be reimplemented when porting. The common routines can be used by any ++ * driver, and some must be used by the end user to control the drivers. For ++ * example, you must use the Parameter common library in order to set the ++ * parameters in the WUDEV module. ++ * ++ * The common libraries consist of the following: ++ * ++ * - Connection Contexts - Used internally and can be used by end-user. See dwc_cc.h ++ * - Parameters - Used internally and can be used by end-user. See dwc_params.h ++ * - Notifications - Used internally and can be used by end-user. See dwc_notifier.h ++ * - Lists - Used internally and can be used by end-user. See dwc_list.h ++ * - Memory Debugging - Used internally and can be used by end-user. See dwc_os.h ++ * - Modpow - Used internally only. See dwc_modpow.h ++ * - DH - Used internally only. See dwc_dh.h ++ * - Crypto - Used internally only. See dwc_crypto.h ++ * ++ * ++ * @section prereq Prerequistes For dwc_os.h ++ * @subsection types Data Types ++ * ++ * The dwc_os.h file assumes that several low-level data types are pre defined for the ++ * compilation environment. These data types are: ++ * ++ * - uint8_t - unsigned 8-bit data type ++ * - int8_t - signed 8-bit data type ++ * - uint16_t - unsigned 16-bit data type ++ * - int16_t - signed 16-bit data type ++ * - uint32_t - unsigned 32-bit data type ++ * - int32_t - signed 32-bit data type ++ * - uint64_t - unsigned 64-bit data type ++ * - int64_t - signed 64-bit data type ++ * ++ * Ensure that these are defined before using dwc_os.h. The easiest way to do ++ * that is to modify the top of the file to include the appropriate header. ++ * This is already done for the Linux environment. If the DWC_LINUX macro is ++ * defined, the correct header will be added. A standard header <stdint.h> is ++ * also used for environments where standard C headers are available. ++ * ++ * @subsection stdarg Variable Arguments ++ * ++ * Variable arguments are provided by a standard C header <stdarg.h>. it is ++ * available in Both the Linux and ANSI C enviornment. An equivalent must be ++ * provided in your enviornment in order to use dwc_os.h with the debug and ++ * tracing message functionality. ++ * ++ * @subsection thread Threading ++ * ++ * WUDEV Core must be run on an operating system that provides for multiple ++ * threads/processes. Threading can be implemented in many ways, even in ++ * embedded systems without an operating system. At the bare minimum, the ++ * system should be able to start any number of processes at any time to handle ++ * special work. It need not be a pre-emptive system. Process context can ++ * change upon a call to a blocking function. The hardware interrupt context ++ * that calls the module's ISR() function must be differentiable from process ++ * context, even if your processes are impemented via a hardware interrupt. ++ * Further locking mechanism between process must exist (or be implemented), and ++ * process context must have a way to disable interrupts for a period of time to ++ * lock them out. If all of this exists, the functions in dwc_os.h related to ++ * threading should be able to be implemented with the defined behavior. ++ * ++ */ +--- /dev/null ++++ b/drivers/usb/host/dwc_common_port/usb.h +@@ -0,0 +1,850 @@ ++/* ++ * Copyright (c) 1998 The NetBSD Foundation, Inc. ++ * All rights reserved. ++ * ++ * This code is derived from software contributed to The NetBSD Foundation ++ * by Lennart Augustsson (lennart@augustsson.net) at ++ * Carlstedt Research & Technology. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions and the following disclaimer. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. All advertising materials mentioning features or use of this software ++ * must display the following acknowledgement: ++ * This product includes software developed by the NetBSD ++ * Foundation, Inc. and its contributors. ++ * 4. Neither the name of The NetBSD Foundation nor the names of its ++ * contributors may be used to endorse or promote products derived ++ * from this software without specific prior written permission. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS ++ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ++ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS ++ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ++ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ++ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ++ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ++ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ++ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ++ * POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++/* Modified by Synopsys, Inc, 12/12/2007 */ ++ ++ ++#ifndef _USB_H_ ++#define _USB_H_ ++ ++#include "dwc_os.h" ++ ++/* ++ * The USB records contain some unaligned little-endian word ++ * components. The U[SG]ETW macros take care of both the alignment ++ * and endian problem and should always be used to access non-byte ++ * values. ++ */ ++typedef u_int8_t uByte; ++typedef u_int8_t uWord[2]; ++typedef u_int8_t uDWord[4]; ++ ++#define USETW2(w,h,l) ((w)[0] = (u_int8_t)(l), (w)[1] = (u_int8_t)(h)) ++ ++#if 1 ++#define UGETW(w) ((w)[0] | ((w)[1] << 8)) ++#define USETW(w,v) ((w)[0] = (u_int8_t)(v), (w)[1] = (u_int8_t)((v) >> 8)) ++#define UGETDW(w) ((w)[0] | ((w)[1] << 8) | ((w)[2] << 16) | ((w)[3] << 24)) ++#define USETDW(w,v) ((w)[0] = (u_int8_t)(v), \ ++ (w)[1] = (u_int8_t)((v) >> 8), \ ++ (w)[2] = (u_int8_t)((v) >> 16), \ ++ (w)[3] = (u_int8_t)((v) >> 24)) ++#else ++/* ++ * On little-endian machines that can handle unanliged accesses ++ * (e.g. i386) these macros can be replaced by the following. ++ */ ++#define UGETW(w) (*(u_int16_t *)(w)) ++#define USETW(w,v) (*(u_int16_t *)(w) = (v)) ++#define UGETDW(w) (*(u_int32_t *)(w)) ++#define USETDW(w,v) (*(u_int32_t *)(w) = (v)) ++#endif ++ ++#define UPACKED __attribute__((__packed__)) ++ ++typedef struct { ++ uByte bmRequestType; ++ uByte bRequest; ++ uWord wValue; ++ uWord wIndex; ++ uWord wLength; ++} UPACKED usb_device_request_t; ++ ++#define UT_GET_DIR(a) ((a) & 0x80) ++#define UT_WRITE 0x00 ++#define UT_READ 0x80 ++ ++#define UT_GET_TYPE(a) ((a) & 0x60) ++#define UT_STANDARD 0x00 ++#define UT_CLASS 0x20 ++#define UT_VENDOR 0x40 ++ ++#define UT_GET_RECIPIENT(a) ((a) & 0x1f) ++#define UT_DEVICE 0x00 ++#define UT_INTERFACE 0x01 ++#define UT_ENDPOINT 0x02 ++#define UT_OTHER 0x03 ++ ++#define UT_READ_DEVICE (UT_READ | UT_STANDARD | UT_DEVICE) ++#define UT_READ_INTERFACE (UT_READ | UT_STANDARD | UT_INTERFACE) ++#define UT_READ_ENDPOINT (UT_READ | UT_STANDARD | UT_ENDPOINT) ++#define UT_WRITE_DEVICE (UT_WRITE | UT_STANDARD | UT_DEVICE) ++#define UT_WRITE_INTERFACE (UT_WRITE | UT_STANDARD | UT_INTERFACE) ++#define UT_WRITE_ENDPOINT (UT_WRITE | UT_STANDARD | UT_ENDPOINT) ++#define UT_READ_CLASS_DEVICE (UT_READ | UT_CLASS | UT_DEVICE) ++#define UT_READ_CLASS_INTERFACE (UT_READ | UT_CLASS | UT_INTERFACE) ++#define UT_READ_CLASS_OTHER (UT_READ | UT_CLASS | UT_OTHER) ++#define UT_READ_CLASS_ENDPOINT (UT_READ | UT_CLASS | UT_ENDPOINT) ++#define UT_WRITE_CLASS_DEVICE (UT_WRITE | UT_CLASS | UT_DEVICE) ++#define UT_WRITE_CLASS_INTERFACE (UT_WRITE | UT_CLASS | UT_INTERFACE) ++#define UT_WRITE_CLASS_OTHER (UT_WRITE | UT_CLASS | UT_OTHER) ++#define UT_WRITE_CLASS_ENDPOINT (UT_WRITE | UT_CLASS | UT_ENDPOINT) ++#define UT_READ_VENDOR_DEVICE (UT_READ | UT_VENDOR | UT_DEVICE) ++#define UT_READ_VENDOR_INTERFACE (UT_READ | UT_VENDOR | UT_INTERFACE) ++#define UT_READ_VENDOR_OTHER (UT_READ | UT_VENDOR | UT_OTHER) ++#define UT_READ_VENDOR_ENDPOINT (UT_READ | UT_VENDOR | UT_ENDPOINT) ++#define UT_WRITE_VENDOR_DEVICE (UT_WRITE | UT_VENDOR | UT_DEVICE) ++#define UT_WRITE_VENDOR_INTERFACE (UT_WRITE | UT_VENDOR | UT_INTERFACE) ++#define UT_WRITE_VENDOR_OTHER (UT_WRITE | UT_VENDOR | UT_OTHER) ++#define UT_WRITE_VENDOR_ENDPOINT (UT_WRITE | UT_VENDOR | UT_ENDPOINT) ++ ++/* Requests */ ++#define UR_GET_STATUS 0x00 ++#define USTAT_STANDARD_STATUS 0x00 ++#define WUSTAT_WUSB_FEATURE 0x01 ++#define WUSTAT_CHANNEL_INFO 0x02 ++#define WUSTAT_RECEIVED_DATA 0x03 ++#define WUSTAT_MAS_AVAILABILITY 0x04 ++#define WUSTAT_CURRENT_TRANSMIT_POWER 0x05 ++#define UR_CLEAR_FEATURE 0x01 ++#define UR_SET_FEATURE 0x03 ++#define UR_SET_AND_TEST_FEATURE 0x0c ++#define UR_SET_ADDRESS 0x05 ++#define UR_GET_DESCRIPTOR 0x06 ++#define UDESC_DEVICE 0x01 ++#define UDESC_CONFIG 0x02 ++#define UDESC_STRING 0x03 ++#define UDESC_INTERFACE 0x04 ++#define UDESC_ENDPOINT 0x05 ++#define UDESC_DEVICE_QUALIFIER 0x06 ++#define UDESC_OTHER_SPEED_CONFIGURATION 0x07 ++#define UDESC_INTERFACE_POWER 0x08 ++#define UDESC_OTG 0x09 ++#define WUDESC_SECURITY 0x0c ++#define WUDESC_KEY 0x0d ++#define WUD_GET_KEY_INDEX(_wValue_) ((_wValue_) & 0xf) ++#define WUD_GET_KEY_TYPE(_wValue_) (((_wValue_) & 0x30) >> 4) ++#define WUD_KEY_TYPE_ASSOC 0x01 ++#define WUD_KEY_TYPE_GTK 0x02 ++#define WUD_GET_KEY_ORIGIN(_wValue_) (((_wValue_) & 0x40) >> 6) ++#define WUD_KEY_ORIGIN_HOST 0x00 ++#define WUD_KEY_ORIGIN_DEVICE 0x01 ++#define WUDESC_ENCRYPTION_TYPE 0x0e ++#define WUDESC_BOS 0x0f ++#define WUDESC_DEVICE_CAPABILITY 0x10 ++#define WUDESC_WIRELESS_ENDPOINT_COMPANION 0x11 ++#define UDESC_CS_DEVICE 0x21 /* class specific */ ++#define UDESC_CS_CONFIG 0x22 ++#define UDESC_CS_STRING 0x23 ++#define UDESC_CS_INTERFACE 0x24 ++#define UDESC_CS_ENDPOINT 0x25 ++#define UDESC_HUB 0x29 ++#define UR_SET_DESCRIPTOR 0x07 ++#define UR_GET_CONFIG 0x08 ++#define UR_SET_CONFIG 0x09 ++#define UR_GET_INTERFACE 0x0a ++#define UR_SET_INTERFACE 0x0b ++#define UR_SYNCH_FRAME 0x0c ++#define WUR_SET_ENCRYPTION 0x0d ++#define WUR_GET_ENCRYPTION 0x0e ++#define WUR_SET_HANDSHAKE 0x0f ++#define WUR_GET_HANDSHAKE 0x10 ++#define WUR_SET_CONNECTION 0x11 ++#define WUR_SET_SECURITY_DATA 0x12 ++#define WUR_GET_SECURITY_DATA 0x13 ++#define WUR_SET_WUSB_DATA 0x14 ++#define WUDATA_DRPIE_INFO 0x01 ++#define WUDATA_TRANSMIT_DATA 0x02 ++#define WUDATA_TRANSMIT_PARAMS 0x03 ++#define WUDATA_RECEIVE_PARAMS 0x04 ++#define WUDATA_TRANSMIT_POWER 0x05 ++#define WUR_LOOPBACK_DATA_WRITE 0x15 ++#define WUR_LOOPBACK_DATA_READ 0x16 ++#define WUR_SET_INTERFACE_DS 0x17 ++ ++/* Feature numbers */ ++#define UF_ENDPOINT_HALT 0 ++#define UF_DEVICE_REMOTE_WAKEUP 1 ++#define UF_TEST_MODE 2 ++#define UF_DEVICE_B_HNP_ENABLE 3 ++#define UF_DEVICE_A_HNP_SUPPORT 4 ++#define UF_DEVICE_A_ALT_HNP_SUPPORT 5 ++#define WUF_WUSB 3 ++#define WUF_TX_DRPIE 0x0 ++#define WUF_DEV_XMIT_PACKET 0x1 ++#define WUF_COUNT_PACKETS 0x2 ++#define WUF_CAPTURE_PACKETS 0x3 ++ ++/* Class requests from the USB 2.0 hub spec, table 11-15 */ ++#define UCR_CLEAR_HUB_FEATURE (0x2000 | UR_CLEAR_FEATURE) ++#define UCR_CLEAR_PORT_FEATURE (0x2300 | UR_CLEAR_FEATURE) ++#define UCR_GET_HUB_DESCRIPTOR (0xa000 | UR_GET_DESCRIPTOR) ++#define UCR_GET_HUB_STATUS (0xa000 | UR_GET_STATUS) ++#define UCR_GET_PORT_STATUS (0xa300 | UR_GET_STATUS) ++#define UCR_SET_HUB_FEATURE (0x2000 | UR_SET_FEATURE) ++#define UCR_SET_PORT_FEATURE (0x2300 | UR_SET_FEATURE) ++#define UCR_SET_AND_TEST_PORT_FEATURE (0xa300 | UR_SET_AND_TEST_FEATURE) ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bDescriptorSubtype; ++} UPACKED usb_descriptor_t; ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord bcdUSB; ++#define UD_USB_2_0 0x0200 ++#define UD_IS_USB2(d) (UGETW((d)->bcdUSB) >= UD_USB_2_0) ++ uByte bDeviceClass; ++ uByte bDeviceSubClass; ++ uByte bDeviceProtocol; ++ uByte bMaxPacketSize; ++ /* The fields below are not part of the initial descriptor. */ ++ uWord idVendor; ++ uWord idProduct; ++ uWord bcdDevice; ++ uByte iManufacturer; ++ uByte iProduct; ++ uByte iSerialNumber; ++ uByte bNumConfigurations; ++} UPACKED usb_device_descriptor_t; ++#define USB_DEVICE_DESCRIPTOR_SIZE 18 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord wTotalLength; ++ uByte bNumInterface; ++ uByte bConfigurationValue; ++ uByte iConfiguration; ++ uByte bmAttributes; ++#define UC_BUS_POWERED 0x80 ++#define UC_SELF_POWERED 0x40 ++#define UC_REMOTE_WAKEUP 0x20 ++ uByte bMaxPower; /* max current in 2 mA units */ ++#define UC_POWER_FACTOR 2 ++} UPACKED usb_config_descriptor_t; ++#define USB_CONFIG_DESCRIPTOR_SIZE 9 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bInterfaceNumber; ++ uByte bAlternateSetting; ++ uByte bNumEndpoints; ++ uByte bInterfaceClass; ++ uByte bInterfaceSubClass; ++ uByte bInterfaceProtocol; ++ uByte iInterface; ++} UPACKED usb_interface_descriptor_t; ++#define USB_INTERFACE_DESCRIPTOR_SIZE 9 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bEndpointAddress; ++#define UE_GET_DIR(a) ((a) & 0x80) ++#define UE_SET_DIR(a,d) ((a) | (((d)&1) << 7)) ++#define UE_DIR_IN 0x80 ++#define UE_DIR_OUT 0x00 ++#define UE_ADDR 0x0f ++#define UE_GET_ADDR(a) ((a) & UE_ADDR) ++ uByte bmAttributes; ++#define UE_XFERTYPE 0x03 ++#define UE_CONTROL 0x00 ++#define UE_ISOCHRONOUS 0x01 ++#define UE_BULK 0x02 ++#define UE_INTERRUPT 0x03 ++#define UE_GET_XFERTYPE(a) ((a) & UE_XFERTYPE) ++#define UE_ISO_TYPE 0x0c ++#define UE_ISO_ASYNC 0x04 ++#define UE_ISO_ADAPT 0x08 ++#define UE_ISO_SYNC 0x0c ++#define UE_GET_ISO_TYPE(a) ((a) & UE_ISO_TYPE) ++ uWord wMaxPacketSize; ++ uByte bInterval; ++} UPACKED usb_endpoint_descriptor_t; ++#define USB_ENDPOINT_DESCRIPTOR_SIZE 7 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord bString[127]; ++} UPACKED usb_string_descriptor_t; ++#define USB_MAX_STRING_LEN 128 ++#define USB_LANGUAGE_TABLE 0 /* # of the string language id table */ ++ ++/* Hub specific request */ ++#define UR_GET_BUS_STATE 0x02 ++#define UR_CLEAR_TT_BUFFER 0x08 ++#define UR_RESET_TT 0x09 ++#define UR_GET_TT_STATE 0x0a ++#define UR_STOP_TT 0x0b ++ ++/* Hub features */ ++#define UHF_C_HUB_LOCAL_POWER 0 ++#define UHF_C_HUB_OVER_CURRENT 1 ++#define UHF_PORT_CONNECTION 0 ++#define UHF_PORT_ENABLE 1 ++#define UHF_PORT_SUSPEND 2 ++#define UHF_PORT_OVER_CURRENT 3 ++#define UHF_PORT_RESET 4 ++#define UHF_PORT_L1 5 ++#define UHF_PORT_POWER 8 ++#define UHF_PORT_LOW_SPEED 9 ++#define UHF_PORT_HIGH_SPEED 10 ++#define UHF_C_PORT_CONNECTION 16 ++#define UHF_C_PORT_ENABLE 17 ++#define UHF_C_PORT_SUSPEND 18 ++#define UHF_C_PORT_OVER_CURRENT 19 ++#define UHF_C_PORT_RESET 20 ++#define UHF_C_PORT_L1 23 ++#define UHF_PORT_TEST 21 ++#define UHF_PORT_INDICATOR 22 ++ ++typedef struct { ++ uByte bDescLength; ++ uByte bDescriptorType; ++ uByte bNbrPorts; ++ uWord wHubCharacteristics; ++#define UHD_PWR 0x0003 ++#define UHD_PWR_GANGED 0x0000 ++#define UHD_PWR_INDIVIDUAL 0x0001 ++#define UHD_PWR_NO_SWITCH 0x0002 ++#define UHD_COMPOUND 0x0004 ++#define UHD_OC 0x0018 ++#define UHD_OC_GLOBAL 0x0000 ++#define UHD_OC_INDIVIDUAL 0x0008 ++#define UHD_OC_NONE 0x0010 ++#define UHD_TT_THINK 0x0060 ++#define UHD_TT_THINK_8 0x0000 ++#define UHD_TT_THINK_16 0x0020 ++#define UHD_TT_THINK_24 0x0040 ++#define UHD_TT_THINK_32 0x0060 ++#define UHD_PORT_IND 0x0080 ++ uByte bPwrOn2PwrGood; /* delay in 2 ms units */ ++#define UHD_PWRON_FACTOR 2 ++ uByte bHubContrCurrent; ++ uByte DeviceRemovable[32]; /* max 255 ports */ ++#define UHD_NOT_REMOV(desc, i) \ ++ (((desc)->DeviceRemovable[(i)/8] >> ((i) % 8)) & 1) ++ /* deprecated */ uByte PortPowerCtrlMask[1]; ++} UPACKED usb_hub_descriptor_t; ++#define USB_HUB_DESCRIPTOR_SIZE 9 /* includes deprecated PortPowerCtrlMask */ ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uWord bcdUSB; ++ uByte bDeviceClass; ++ uByte bDeviceSubClass; ++ uByte bDeviceProtocol; ++ uByte bMaxPacketSize0; ++ uByte bNumConfigurations; ++ uByte bReserved; ++} UPACKED usb_device_qualifier_t; ++#define USB_DEVICE_QUALIFIER_SIZE 10 ++ ++typedef struct { ++ uByte bLength; ++ uByte bDescriptorType; ++ uByte bmAttributes; ++#define UOTG_SRP 0x01 ++#define UOTG_HNP 0x02 ++} UPACKED usb_otg_descriptor_t; ++ ++/* OTG feature selectors */ ++#define UOTG_B_HNP_ENABLE 3 ++#define UOTG_A_HNP_SUPPORT 4 ++#define UOTG_A_ALT_HNP_SUPPORT 5 ++ ++typedef struct { ++ uWord wStatus; ++/* Device status flags */ ++#define UDS_SELF_POWERED 0x0001 ++#define UDS_REMOTE_WAKEUP 0x0002 ++/* Endpoint status flags */ ++#define UES_HALT 0x0001 ++} UPACKED usb_status_t; ++ ++typedef struct { ++ uWord wHubStatus; ++#define UHS_LOCAL_POWER 0x0001 ++#define UHS_OVER_CURRENT 0x0002 ++ uWord wHubChange; ++} UPACKED usb_hub_status_t; ++ ++typedef struct { ++ uWord wPortStatus; ++#define UPS_CURRENT_CONNECT_STATUS 0x0001 ++#define UPS_PORT_ENABLED 0x0002 ++#define UPS_SUSPEND 0x0004 ++#define UPS_OVERCURRENT_INDICATOR 0x0008 ++#define UPS_RESET 0x0010 ++#define UPS_PORT_POWER 0x0100 ++#define UPS_LOW_SPEED 0x0200 ++#define UPS_HIGH_SPEED 0x0400 ++#define UPS_PORT_TEST 0x0800 ++#define UPS_PORT_INDICATOR 0x1000 ++ uWord wPortChange; ++#define UPS_C_CONNECT_STATUS 0x0001 ++#define UPS_C_PORT_ENABLED 0x0002 ++#define UPS_C_SUSPEND 0x0004 ++#define UPS_C_OVERCURRENT_INDICATOR 0x0008 ++#define UPS_C_PORT_RESET 0x0010 ++} UPACKED usb_port_status_t; ++ ++/* Device class codes */ ++#define UDCLASS_IN_INTERFACE 0x00 ++#define UDCLASS_COMM 0x02 ++#define UDCLASS_HUB 0x09 ++#define UDSUBCLASS_HUB 0x00 ++#define UDPROTO_FSHUB 0x00 ++#define UDPROTO_HSHUBSTT 0x01 ++#define UDPROTO_HSHUBMTT 0x02 ++#define UDCLASS_DIAGNOSTIC 0xdc ++#define UDCLASS_WIRELESS 0xe0 ++#define UDSUBCLASS_RF 0x01 ++#define UDPROTO_BLUETOOTH 0x01 ++#define UDCLASS_VENDOR 0xff ++ ++/* Interface class codes */ ++#define UICLASS_UNSPEC 0x00 ++ ++#define UICLASS_AUDIO 0x01 ++#define UISUBCLASS_AUDIOCONTROL 1 ++#define UISUBCLASS_AUDIOSTREAM 2 ++#define UISUBCLASS_MIDISTREAM 3 ++ ++#define UICLASS_CDC 0x02 /* communication */ ++#define UISUBCLASS_DIRECT_LINE_CONTROL_MODEL 1 ++#define UISUBCLASS_ABSTRACT_CONTROL_MODEL 2 ++#define UISUBCLASS_TELEPHONE_CONTROL_MODEL 3 ++#define UISUBCLASS_MULTICHANNEL_CONTROL_MODEL 4 ++#define UISUBCLASS_CAPI_CONTROLMODEL 5 ++#define UISUBCLASS_ETHERNET_NETWORKING_CONTROL_MODEL 6 ++#define UISUBCLASS_ATM_NETWORKING_CONTROL_MODEL 7 ++#define UIPROTO_CDC_AT 1 ++ ++#define UICLASS_HID 0x03 ++#define UISUBCLASS_BOOT 1 ++#define UIPROTO_BOOT_KEYBOARD 1 ++ ++#define UICLASS_PHYSICAL 0x05 ++ ++#define UICLASS_IMAGE 0x06 ++ ++#define UICLASS_PRINTER 0x07 ++#define UISUBCLASS_PRINTER 1 ++#define UIPROTO_PRINTER_UNI 1 ++#define UIPROTO_PRINTER_BI 2 ++#define UIPROTO_PRINTER_1284 3 ++ ++#define UICLASS_MASS 0x08 ++#define UISUBCLASS_RBC 1 ++#define UISUBCLASS_SFF8020I 2 ++#define UISUBCLASS_QIC157 3 ++#define UISUBCLASS_UFI 4 ++#define UISUBCLASS_SFF8070I 5 ++#define UISUBCLASS_SCSI 6 ++#define UIPROTO_MASS_CBI_I 0 ++#define UIPROTO_MASS_CBI 1 ++#define UIPROTO_MASS_BBB_OLD 2 /* Not in the spec anymore */ ++#define UIPROTO_MASS_BBB 80 /* 'P' for the Iomega Zip drive */ ++ ++#define UICLASS_HUB 0x09 ++#define UISUBCLASS_HUB 0 ++#define UIPROTO_FSHUB 0 ++#define UIPROTO_HSHUBSTT 0 /* Yes, same as previous */ ++#define UIPROTO_HSHUBMTT 1 ++ ++#define UICLASS_CDC_DATA 0x0a ++#define UISUBCLASS_DATA 0 ++#define UIPROTO_DATA_ISDNBRI 0x30 /* Physical iface */ ++#define UIPROTO_DATA_HDLC 0x31 /* HDLC */ ++#define UIPROTO_DATA_TRANSPARENT 0x32 /* Transparent */ ++#define UIPROTO_DATA_Q921M 0x50 /* Management for Q921 */ ++#define UIPROTO_DATA_Q921 0x51 /* Data for Q921 */ ++#define UIPROTO_DATA_Q921TM 0x52 /* TEI multiplexer for Q921 */ ++#define UIPROTO_DATA_V42BIS 0x90 /* Data compression */ ++#define UIPROTO_DATA_Q931 0x91 /* Euro-ISDN */ ++#define UIPROTO_DATA_V120 0x92 /* V.24 rate adaption */ ++#define UIPROTO_DATA_CAPI 0x93 /* CAPI 2.0 commands */ ++#define UIPROTO_DATA_HOST_BASED 0xfd /* Host based driver */ ++#define UIPROTO_DATA_PUF 0xfe /* see Prot. Unit Func. Desc.*/ ++#define UIPROTO_DATA_VENDOR 0xff /* Vendor specific */ ++ ++#define UICLASS_SMARTCARD 0x0b ++ ++/*#define UICLASS_FIRM_UPD 0x0c*/ ++ ++#define UICLASS_SECURITY 0x0d ++ ++#define UICLASS_DIAGNOSTIC 0xdc ++ ++#define UICLASS_WIRELESS 0xe0 ++#define UISUBCLASS_RF 0x01 ++#define UIPROTO_BLUETOOTH 0x01 ++ ++#define UICLASS_APPL_SPEC 0xfe ++#define UISUBCLASS_FIRMWARE_DOWNLOAD 1 ++#define UISUBCLASS_IRDA 2 ++#define UIPROTO_IRDA 0 ++ ++#define UICLASS_VENDOR 0xff ++ ++ ++#define USB_HUB_MAX_DEPTH 5 ++ ++/* ++ * Minimum time a device needs to be powered down to go through ++ * a power cycle. XXX Are these time in the spec? ++ */ ++#define USB_POWER_DOWN_TIME 200 /* ms */ ++#define USB_PORT_POWER_DOWN_TIME 100 /* ms */ ++ ++#if 0 ++/* These are the values from the spec. */ ++#define USB_PORT_RESET_DELAY 10 /* ms */ ++#define USB_PORT_ROOT_RESET_DELAY 50 /* ms */ ++#define USB_PORT_RESET_RECOVERY 10 /* ms */ ++#define USB_PORT_POWERUP_DELAY 100 /* ms */ ++#define USB_SET_ADDRESS_SETTLE 2 /* ms */ ++#define USB_RESUME_DELAY (20*5) /* ms */ ++#define USB_RESUME_WAIT 10 /* ms */ ++#define USB_RESUME_RECOVERY 10 /* ms */ ++#define USB_EXTRA_POWER_UP_TIME 0 /* ms */ ++#else ++/* Allow for marginal (i.e. non-conforming) devices. */ ++#define USB_PORT_RESET_DELAY 50 /* ms */ ++#define USB_PORT_ROOT_RESET_DELAY 250 /* ms */ ++#define USB_PORT_RESET_RECOVERY 250 /* ms */ ++#define USB_PORT_POWERUP_DELAY 300 /* ms */ ++#define USB_SET_ADDRESS_SETTLE 10 /* ms */ ++#define USB_RESUME_DELAY (50*5) /* ms */ ++#define USB_RESUME_WAIT 50 /* ms */ ++#define USB_RESUME_RECOVERY 50 /* ms */ ++#define USB_EXTRA_POWER_UP_TIME 20 /* ms */ ++#endif ++ ++#define USB_MIN_POWER 100 /* mA */ ++#define USB_MAX_POWER 500 /* mA */ ++ ++#define USB_BUS_RESET_DELAY 100 /* ms XXX?*/ ++ ++ ++#define USB_UNCONFIG_NO 0 ++#define USB_UNCONFIG_INDEX (-1) ++ ++/*** ioctl() related stuff ***/ ++ ++struct usb_ctl_request { ++ int ucr_addr; ++ usb_device_request_t ucr_request; ++ void *ucr_data; ++ int ucr_flags; ++#define USBD_SHORT_XFER_OK 0x04 /* allow short reads */ ++ int ucr_actlen; /* actual length transferred */ ++}; ++ ++struct usb_alt_interface { ++ int uai_config_index; ++ int uai_interface_index; ++ int uai_alt_no; ++}; ++ ++#define USB_CURRENT_CONFIG_INDEX (-1) ++#define USB_CURRENT_ALT_INDEX (-1) ++ ++struct usb_config_desc { ++ int ucd_config_index; ++ usb_config_descriptor_t ucd_desc; ++}; ++ ++struct usb_interface_desc { ++ int uid_config_index; ++ int uid_interface_index; ++ int uid_alt_index; ++ usb_interface_descriptor_t uid_desc; ++}; ++ ++struct usb_endpoint_desc { ++ int ued_config_index; ++ int ued_interface_index; ++ int ued_alt_index; ++ int ued_endpoint_index; ++ usb_endpoint_descriptor_t ued_desc; ++}; ++ ++struct usb_full_desc { ++ int ufd_config_index; ++ u_int ufd_size; ++ u_char *ufd_data; ++}; ++ ++struct usb_string_desc { ++ int usd_string_index; ++ int usd_language_id; ++ usb_string_descriptor_t usd_desc; ++}; ++ ++struct usb_ctl_report_desc { ++ int ucrd_size; ++ u_char ucrd_data[1024]; /* filled data size will vary */ ++}; ++ ++typedef struct { u_int32_t cookie; } usb_event_cookie_t; ++ ++#define USB_MAX_DEVNAMES 4 ++#define USB_MAX_DEVNAMELEN 16 ++struct usb_device_info { ++ u_int8_t udi_bus; ++ u_int8_t udi_addr; /* device address */ ++ usb_event_cookie_t udi_cookie; ++ char udi_product[USB_MAX_STRING_LEN]; ++ char udi_vendor[USB_MAX_STRING_LEN]; ++ char udi_release[8]; ++ u_int16_t udi_productNo; ++ u_int16_t udi_vendorNo; ++ u_int16_t udi_releaseNo; ++ u_int8_t udi_class; ++ u_int8_t udi_subclass; ++ u_int8_t udi_protocol; ++ u_int8_t udi_config; ++ u_int8_t udi_speed; ++#define USB_SPEED_LOW 1 ++#define USB_SPEED_FULL 2 ++#define USB_SPEED_HIGH 3 ++ int udi_power; /* power consumption in mA, 0 if selfpowered */ ++ int udi_nports; ++ char udi_devnames[USB_MAX_DEVNAMES][USB_MAX_DEVNAMELEN]; ++ u_int8_t udi_ports[16];/* hub only: addresses of devices on ports */ ++#define USB_PORT_ENABLED 0xff ++#define USB_PORT_SUSPENDED 0xfe ++#define USB_PORT_POWERED 0xfd ++#define USB_PORT_DISABLED 0xfc ++}; ++ ++struct usb_ctl_report { ++ int ucr_report; ++ u_char ucr_data[1024]; /* filled data size will vary */ ++}; ++ ++struct usb_device_stats { ++ u_long uds_requests[4]; /* indexed by transfer type UE_* */ ++}; ++ ++ ++ ++ ++#define WUSB_MIN_IE 0x80 ++#define WUSB_WCTA_IE 0x80 ++#define WUSB_WCONNECTACK_IE 0x81 ++#define WUSB_WHOSTINFO_IE 0x82 ++#define WUHI_GET_CA(_bmAttributes_) ((_bmAttributes_) & 0x3) ++#define WUHI_CA_RECONN 0x00 ++#define WUHI_CA_LIMITED 0x01 ++#define WUHI_CA_ALL 0x03 ++#define WUHI_GET_MLSI(_bmAttributes_) (((_bmAttributes_) & 0x38) >> 3) ++#define WUSB_WCHCHANGEANNOUNCE_IE 0x83 ++#define WUSB_WDEV_DISCONNECT_IE 0x84 ++#define WUSB_WHOST_DISCONNECT_IE 0x85 ++#define WUSB_WRELEASE_CHANNEL_IE 0x86 ++#define WUSB_WWORK_IE 0x87 ++#define WUSB_WCHANNEL_STOP_IE 0x88 ++#define WUSB_WDEV_KEEPALIVE_IE 0x89 ++#define WUSB_WISOCH_DISCARD_IE 0x8A ++#define WUSB_WRESETDEVICE_IE 0x8B ++#define WUSB_WXMIT_PACKET_ADJUST_IE 0x8C ++#define WUSB_MAX_IE 0x8C ++ ++/* Device Notification Types */ ++ ++#define WUSB_DN_MIN 0x01 ++#define WUSB_DN_CONNECT 0x01 ++# define WUSB_DA_OLDCONN 0x00 ++# define WUSB_DA_NEWCONN 0x01 ++# define WUSB_DA_SELF_BEACON 0x02 ++# define WUSB_DA_DIR_BEACON 0x04 ++# define WUSB_DA_NO_BEACON 0x06 ++#define WUSB_DN_DISCONNECT 0x02 ++#define WUSB_DN_EPRDY 0x03 ++#define WUSB_DN_MASAVAILCHANGED 0x04 ++#define WUSB_DN_REMOTEWAKEUP 0x05 ++#define WUSB_DN_SLEEP 0x06 ++#define WUSB_DN_ALIVE 0x07 ++#define WUSB_DN_MAX 0x07 ++ ++ ++/* WUSB Handshake Data. Used during the SET/GET HANDSHAKE requests */ ++typedef struct wusb_hndshk_data { ++ uint8_t bMessageNumber; ++ uint8_t bStatus; ++ uint8_t tTKID[3]; ++ uint8_t bReserved; ++ uint8_t CDID[16]; ++ uint8_t Nonce[16]; ++ uint8_t MIC[8]; ++} UPACKED wusb_hndshk_data_t; ++#define WUSB_HANDSHAKE_LEN_FOR_MIC 38 ++ ++/* WUSB Connection Context */ ++typedef struct wusb_conn_context { ++ uint8_t CHID [16]; ++ uint8_t CDID [16]; ++ uint8_t CK [16]; ++} UPACKED wusb_conn_context_t; ++ ++/* WUSB Security Descriptor */ ++typedef struct wusb_security_desc { ++ uint8_t bLength; ++ uint8_t bDescriptorType; ++ uint16_t wTotalLength; ++ uint8_t bNumEncryptionTypes; ++} UPACKED wusb_security_desc_t; ++ ++/* WUSB Encryption Type Descriptor */ ++typedef struct wusb_encrypt_type_desc { ++ uint8_t bLength; ++ uint8_t bDescriptorType; ++ ++ uint8_t bEncryptionType; ++#define WUETD_UNSECURE 0 ++#define WUETD_WIRED 1 ++#define WUETD_CCM_1 2 ++#define WUETD_RSA_1 3 ++ ++ uint8_t bEncryptionValue; ++ uint8_t bAuthKeyIndex; ++} UPACKED wusb_encrypt_type_desc_t; ++ ++/* WUSB Key Descriptor */ ++typedef struct wusb_key_desc { ++ uint8_t bLength; ++ uint8_t bDescriptorType; ++ uint8_t tTKID[3]; ++ uint8_t bReserved; ++ uint8_t KeyData[1]; /* variable length */ ++} UPACKED wusb_key_desc_t; ++ ++/* WUSB BOS Descriptor (Binary device Object Store) */ ++typedef struct wusb_bos_desc { ++ uint8_t bLength; ++ uint8_t bDescriptorType; ++ uint16_t wTotalLength; ++ uint8_t bNumDeviceCaps; ++} UPACKED wusb_bos_desc_t; ++ ++ ++/* Device Capability Type Codes */ ++#define WUSB_DEVICE_CAPABILITY_WIRELESS_USB 0x01 ++ ++/* Device Capability Descriptor */ ++typedef struct wusb_dev_cap_desc { ++ uint8_t bLength; ++ uint8_t bDescriptorType; ++ uint8_t bDevCapabilityType; ++ uint8_t caps[1]; /* Variable length */ ++} UPACKED wusb_dev_cap_desc_t; ++ ++/* Device Capability Descriptor */ ++typedef struct wusb_dev_cap_uwb_desc { ++ uint8_t bLength; ++ uint8_t bDescriptorType; ++ uint8_t bDevCapabilityType; ++ uint8_t bmAttributes; ++ uint16_t wPHYRates; /* Bitmap */ ++ uint8_t bmTFITXPowerInfo; ++ uint8_t bmFFITXPowerInfo; ++ uint16_t bmBandGroup; ++ uint8_t bReserved; ++} UPACKED wusb_dev_cap_uwb_desc_t; ++ ++/* Wireless USB Endpoint Companion Descriptor */ ++typedef struct wusb_endpoint_companion_desc { ++ uint8_t bLength; ++ uint8_t bDescriptorType; ++ uint8_t bMaxBurst; ++ uint8_t bMaxSequence; ++ uint16_t wMaxStreamDelay; ++ uint16_t wOverTheAirPacketSize; ++ uint8_t bOverTheAirInterval; ++ uint8_t bmCompAttributes; ++} UPACKED wusb_endpoint_companion_desc_t; ++ ++ ++/* Wireless USB Numeric Association M1 Data Structure */ ++typedef struct wusb_m1_data { ++ uint8_t version; ++ uint16_t langId; ++ uint8_t deviceFriendlyNameLength; ++ uint8_t sha_256_m3[32]; ++ uint8_t deviceFriendlyName[256]; ++} UPACKED wusb_m1_data_t; ++ ++typedef struct wusb_m2_data { ++ uint8_t version; ++ uint16_t langId; ++ uint8_t hostFriendlyNameLength; ++ uint8_t pkh[384]; ++ uint8_t hostFriendlyName[256]; ++} UPACKED wusb_m2_data_t; ++ ++typedef struct wusb_m3_data { ++ uint8_t pkd[384]; ++ uint8_t nd; ++} UPACKED wusb_m3_data_t; ++ ++typedef struct wusb_m4_data { ++ uint32_t _attributeTypeIdAndLength_1; ++ uint16_t associationTypeId; ++ ++ uint32_t _attributeTypeIdAndLength_2; ++ uint16_t associationSubTypeId; ++ ++ uint32_t _attributeTypeIdAndLength_3; ++ uint32_t length; ++ ++ uint32_t _attributeTypeIdAndLength_4; ++ uint32_t associationStatus; ++ ++ uint32_t _attributeTypeIdAndLength_5; ++ uint8_t chid[16]; ++ ++ uint32_t _attributeTypeIdAndLength_6; ++ uint8_t cdid[16]; ++ ++ uint32_t _attributeTypeIdAndLength_7; ++ uint8_t bandGroups[2]; ++} UPACKED wusb_m4_data_t; ++ ++ ++ ++ ++#endif /* _USB_H_ */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/Makefile +@@ -0,0 +1,78 @@ ++# ++# Makefile for DWC_otg Highspeed USB controller driver ++# ++ ++ifneq ($(KERNELRELEASE),) ++ ++ifeq ($(BUS_INTERFACE),) ++ # BUS_INTERFACE = -DLM_INTERFACE ++ BUS_INTERFACE = -DPLATFORM_INTERFACE=1 ++endif ++ ++CPPFLAGS += -DDEBUG ++ ++# Use one of the following flags to compile the software in host-only or ++# device-only mode. ++#CPPFLAGS += -DDWC_HOST_ONLY ++#CPPFLAGS += -DDWC_DEVICE_ONLY ++ ++CPPFLAGS += -Dlinux -DDWC_HS_ELECT_TST ++#CGG: CPPFLAGS += -DDWC_EN_ISOC ++CPPFLAGS += -I$(obj)/../dwc_common_port ++#CPPFLAGS += -I$(PORTLIB) ++CPPFLAGS += -DDWC_LINUX ++CPPFLAGS += $(CFI) ++CPPFLAGS += $(BUS_INTERFACE) ++ ++obj-$(CONFIG_USB_DWCOTG) += dwc_otg.o ++ ++dwc_otg-objs := dwc_otg_driver.o dwc_otg_attr.o ++dwc_otg-objs += dwc_otg_cil.o dwc_otg_cil_intr.o ++dwc_otg-objs += dwc_otg_pcd_linux.o dwc_otg_pcd.o dwc_otg_pcd_intr.o ++dwc_otg-objs += dwc_otg_hcd.o dwc_otg_hcd_linux.o dwc_otg_hcd_intr.o dwc_otg_hcd_queue.o dwc_otg_hcd_ddma.o ++ifneq ($(CFI),) ++dwc_otg-objs += dwc_otg_cfi.o ++endif ++ ++kernrelwd := $(subst ., ,$(KERNELRELEASE)) ++kernrel3 := $(word 1,$(kernrelwd)).$(word 2,$(kernrelwd)).$(word 3,$(kernrelwd)) ++ ++ifneq ($(kernrel3),2.6.20) ++EXTRA_CFLAGS += $(CPPFLAGS) ++endif ++ ++else ++ ++PWD := $(shell pwd) ++PORTLIB := $(PWD)/../dwc_common_port ++ ++# Command paths ++CTAGS := $(CTAGS) ++DOXYGEN := $(DOXYGEN) ++ ++default: portlib ++ $(MAKE) -C$(KDIR) M=$(PWD) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules ++ ++install: default ++ifneq ($(INSTALL_MOD_PATH),) ++ $(MAKE) -C$(KDIR) M=$(PORTLIB) modules_install ++ $(MAKE) -C$(KDIR) M=$(PWD) modules_install ++else ++ @echo "No install path defined" ++endif ++ ++portlib: ++ $(MAKE) -C$(KDIR) M=$(PORTLIB) ARCH=$(ARCH) CROSS_COMPILE=$(CROSS_COMPILE) modules ++ cp $(PORTLIB)/Module.symvers $(PWD)/ ++ ++docs: $(wildcard *.[hc]) doc/doxygen.cfg ++ $(DOXYGEN) doc/doxygen.cfg ++ ++tags: $(wildcard *.[hc]) ++ $(CTAGS) -e $(wildcard *.[hc]) $(wildcard linux/*.[hc]) $(wildcard $(KDIR)/include/linux/usb*.h) ++ ++ ++clean: ++ rm -rf *.o *.ko .*cmd *.mod.c .tmp_versions ++ ++endif +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dummy_audio.c +@@ -0,0 +1,1575 @@ ++/* ++ * zero.c -- Gadget Zero, for USB development ++ * ++ * Copyright (C) 2003-2004 David Brownell ++ * All rights reserved. ++ * ++ * Redistribution and use in source and binary forms, with or without ++ * modification, are permitted provided that the following conditions ++ * are met: ++ * 1. Redistributions of source code must retain the above copyright ++ * notice, this list of conditions, and the following disclaimer, ++ * without modification. ++ * 2. Redistributions in binary form must reproduce the above copyright ++ * notice, this list of conditions and the following disclaimer in the ++ * documentation and/or other materials provided with the distribution. ++ * 3. The names of the above-listed copyright holders may not be used ++ * to endorse or promote products derived from this software without ++ * specific prior written permission. ++ * ++ * ALTERNATIVELY, this software may be distributed under the terms of the ++ * GNU General Public License ("GPL") as published by the Free Software ++ * Foundation, either version 2 of that License or (at your option) any ++ * later version. ++ * ++ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS ++ * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, ++ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ++ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR ++ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, ++ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, ++ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR ++ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF ++ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING ++ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS ++ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ++ */ ++ ++ ++/* ++ * Gadget Zero only needs two bulk endpoints, and is an example of how you ++ * can write a hardware-agnostic gadget driver running inside a USB device. ++ * ++ * Hardware details are visible (see CONFIG_USB_ZERO_* below) but don't ++ * affect most of the driver. ++ * ++ * Use it with the Linux host/master side "usbtest" driver to get a basic ++ * functional test of your device-side usb stack, or with "usb-skeleton". ++ * ++ * It supports two similar configurations. One sinks whatever the usb host ++ * writes, and in return sources zeroes. The other loops whatever the host ++ * writes back, so the host can read it. Module options include: ++ * ++ * buflen=N default N=4096, buffer size used ++ * qlen=N default N=32, how many buffers in the loopback queue ++ * loopdefault default false, list loopback config first ++ * ++ * Many drivers will only have one configuration, letting them be much ++ * simpler if they also don't support high speed operation (like this ++ * driver does). ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/delay.h> ++#include <linux/ioport.h> ++#include <linux/sched.h> ++#include <linux/slab.h> ++#include <linux/smp_lock.h> ++#include <linux/errno.h> ++#include <linux/init.h> ++#include <linux/timer.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/uts.h> ++#include <linux/version.h> ++#include <linux/device.h> ++#include <linux/moduleparam.h> ++#include <linux/proc_fs.h> ++ ++#include <asm/byteorder.h> ++#include <asm/io.h> ++#include <asm/irq.h> ++#include <asm/system.h> ++#include <asm/unaligned.h> ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,21) ++# include <linux/usb/ch9.h> ++#else ++# include <linux/usb_ch9.h> ++#endif ++ ++#include <linux/usb_gadget.h> ++ ++ ++/*-------------------------------------------------------------------------*/ ++/*-------------------------------------------------------------------------*/ ++ ++ ++static int utf8_to_utf16le(const char *s, u16 *cp, unsigned len) ++{ ++ int count = 0; ++ u8 c; ++ u16 uchar; ++ ++ /* this insists on correct encodings, though not minimal ones. ++ * BUT it currently rejects legit 4-byte UTF-8 code points, ++ * which need surrogate pairs. (Unicode 3.1 can use them.) ++ */ ++ while (len != 0 && (c = (u8) *s++) != 0) { ++ if (unlikely(c & 0x80)) { ++ // 2-byte sequence: ++ // 00000yyyyyxxxxxx = 110yyyyy 10xxxxxx ++ if ((c & 0xe0) == 0xc0) { ++ uchar = (c & 0x1f) << 6; ++ ++ c = (u8) *s++; ++ if ((c & 0xc0) != 0xc0) ++ goto fail; ++ c &= 0x3f; ++ uchar |= c; ++ ++ // 3-byte sequence (most CJKV characters): ++ // zzzzyyyyyyxxxxxx = 1110zzzz 10yyyyyy 10xxxxxx ++ } else if ((c & 0xf0) == 0xe0) { ++ uchar = (c & 0x0f) << 12; ++ ++ c = (u8) *s++; ++ if ((c & 0xc0) != 0xc0) ++ goto fail; ++ c &= 0x3f; ++ uchar |= c << 6; ++ ++ c = (u8) *s++; ++ if ((c & 0xc0) != 0xc0) ++ goto fail; ++ c &= 0x3f; ++ uchar |= c; ++ ++ /* no bogus surrogates */ ++ if (0xd800 <= uchar && uchar <= 0xdfff) ++ goto fail; ++ ++ // 4-byte sequence (surrogate pairs, currently rare): ++ // 11101110wwwwzzzzyy + 110111yyyyxxxxxx ++ // = 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx ++ // (uuuuu = wwww + 1) ++ // FIXME accept the surrogate code points (only) ++ ++ } else ++ goto fail; ++ } else ++ uchar = c; ++ put_unaligned (cpu_to_le16 (uchar), cp++); ++ count++; ++ len--; ++ } ++ return count; ++fail: ++ return -1; ++} ++ ++ ++/** ++ * usb_gadget_get_string - fill out a string descriptor ++ * @table: of c strings encoded using UTF-8 ++ * @id: string id, from low byte of wValue in get string descriptor ++ * @buf: at least 256 bytes ++ * ++ * Finds the UTF-8 string matching the ID, and converts it into a ++ * string descriptor in utf16-le. ++ * Returns length of descriptor (always even) or negative errno ++ * ++ * If your driver needs stings in multiple languages, you'll probably ++ * "switch (wIndex) { ... }" in your ep0 string descriptor logic, ++ * using this routine after choosing which set of UTF-8 strings to use. ++ * Note that US-ASCII is a strict subset of UTF-8; any string bytes with ++ * the eighth bit set will be multibyte UTF-8 characters, not ISO-8859/1 ++ * characters (which are also widely used in C strings). ++ */ ++int ++usb_gadget_get_string (struct usb_gadget_strings *table, int id, u8 *buf) ++{ ++ struct usb_string *s; ++ int len; ++ ++ /* descriptor 0 has the language id */ ++ if (id == 0) { ++ buf [0] = 4; ++ buf [1] = USB_DT_STRING; ++ buf [2] = (u8) table->language; ++ buf [3] = (u8) (table->language >> 8); ++ return 4; ++ } ++ for (s = table->strings; s && s->s; s++) ++ if (s->id == id) ++ break; ++ ++ /* unrecognized: stall. */ ++ if (!s || !s->s) ++ return -EINVAL; ++ ++ /* string descriptors have length, tag, then UTF16-LE text */ ++ len = min ((size_t) 126, strlen (s->s)); ++ memset (buf + 2, 0, 2 * len); /* zero all the bytes */ ++ len = utf8_to_utf16le(s->s, (u16 *)&buf[2], len); ++ if (len < 0) ++ return -EINVAL; ++ buf [0] = (len + 1) * 2; ++ buf [1] = USB_DT_STRING; ++ return buf [0]; ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++/*-------------------------------------------------------------------------*/ ++ ++ ++/** ++ * usb_descriptor_fillbuf - fill buffer with descriptors ++ * @buf: Buffer to be filled ++ * @buflen: Size of buf ++ * @src: Array of descriptor pointers, terminated by null pointer. ++ * ++ * Copies descriptors into the buffer, returning the length or a ++ * negative error code if they can't all be copied. Useful when ++ * assembling descriptors for an associated set of interfaces used ++ * as part of configuring a composite device; or in other cases where ++ * sets of descriptors need to be marshaled. ++ */ ++int ++usb_descriptor_fillbuf(void *buf, unsigned buflen, ++ const struct usb_descriptor_header **src) ++{ ++ u8 *dest = buf; ++ ++ if (!src) ++ return -EINVAL; ++ ++ /* fill buffer from src[] until null descriptor ptr */ ++ for (; 0 != *src; src++) { ++ unsigned len = (*src)->bLength; ++ ++ if (len > buflen) ++ return -EINVAL; ++ memcpy(dest, *src, len); ++ buflen -= len; ++ dest += len; ++ } ++ return dest - (u8 *)buf; ++} ++ ++ ++/** ++ * usb_gadget_config_buf - builts a complete configuration descriptor ++ * @config: Header for the descriptor, including characteristics such ++ * as power requirements and number of interfaces. ++ * @desc: Null-terminated vector of pointers to the descriptors (interface, ++ * endpoint, etc) defining all functions in this device configuration. ++ * @buf: Buffer for the resulting configuration descriptor. ++ * @length: Length of buffer. If this is not big enough to hold the ++ * entire configuration descriptor, an error code will be returned. ++ * ++ * This copies descriptors into the response buffer, building a descriptor ++ * for that configuration. It returns the buffer length or a negative ++ * status code. The config.wTotalLength field is set to match the length ++ * of the result, but other descriptor fields (including power usage and ++ * interface count) must be set by the caller. ++ * ++ * Gadget drivers could use this when constructing a config descriptor ++ * in response to USB_REQ_GET_DESCRIPTOR. They will need to patch the ++ * resulting bDescriptorType value if USB_DT_OTHER_SPEED_CONFIG is needed. ++ */ ++int usb_gadget_config_buf( ++ const struct usb_config_descriptor *config, ++ void *buf, ++ unsigned length, ++ const struct usb_descriptor_header **desc ++) ++{ ++ struct usb_config_descriptor *cp = buf; ++ int len; ++ ++ /* config descriptor first */ ++ if (length < USB_DT_CONFIG_SIZE || !desc) ++ return -EINVAL; ++ *cp = *config; ++ ++ /* then interface/endpoint/class/vendor/... */ ++ len = usb_descriptor_fillbuf(USB_DT_CONFIG_SIZE + (u8*)buf, ++ length - USB_DT_CONFIG_SIZE, desc); ++ if (len < 0) ++ return len; ++ len += USB_DT_CONFIG_SIZE; ++ if (len > 0xffff) ++ return -EINVAL; ++ ++ /* patch up the config descriptor */ ++ cp->bLength = USB_DT_CONFIG_SIZE; ++ cp->bDescriptorType = USB_DT_CONFIG; ++ cp->wTotalLength = cpu_to_le16(len); ++ cp->bmAttributes |= USB_CONFIG_ATT_ONE; ++ return len; ++} ++ ++/*-------------------------------------------------------------------------*/ ++/*-------------------------------------------------------------------------*/ ++ ++ ++#define RBUF_LEN (1024*1024) ++static int rbuf_start; ++static int rbuf_len; ++static __u8 rbuf[RBUF_LEN]; ++ ++/*-------------------------------------------------------------------------*/ ++ ++#define DRIVER_VERSION "St Patrick's Day 2004" ++ ++static const char shortname [] = "zero"; ++static const char longname [] = "YAMAHA YST-MS35D USB Speaker "; ++ ++static const char source_sink [] = "source and sink data"; ++static const char loopback [] = "loop input to output"; ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * driver assumes self-powered hardware, and ++ * has no way for users to trigger remote wakeup. ++ * ++ * this version autoconfigures as much as possible, ++ * which is reasonable for most "bulk-only" drivers. ++ */ ++static const char *EP_IN_NAME; /* source */ ++static const char *EP_OUT_NAME; /* sink */ ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* big enough to hold our biggest descriptor */ ++#define USB_BUFSIZ 512 ++ ++struct zero_dev { ++ spinlock_t lock; ++ struct usb_gadget *gadget; ++ struct usb_request *req; /* for control responses */ ++ ++ /* when configured, we have one of two configs: ++ * - source data (in to host) and sink it (out from host) ++ * - or loop it back (out from host back in to host) ++ */ ++ u8 config; ++ struct usb_ep *in_ep, *out_ep; ++ ++ /* autoresume timer */ ++ struct timer_list resume; ++}; ++ ++#define xprintk(d,level,fmt,args...) \ ++ dev_printk(level , &(d)->gadget->dev , fmt , ## args) ++ ++#ifdef DEBUG ++#define DBG(dev,fmt,args...) \ ++ xprintk(dev , KERN_DEBUG , fmt , ## args) ++#else ++#define DBG(dev,fmt,args...) \ ++ do { } while (0) ++#endif /* DEBUG */ ++ ++#ifdef VERBOSE ++#define VDBG DBG ++#else ++#define VDBG(dev,fmt,args...) \ ++ do { } while (0) ++#endif /* VERBOSE */ ++ ++#define ERROR(dev,fmt,args...) \ ++ xprintk(dev , KERN_ERR , fmt , ## args) ++#define WARN(dev,fmt,args...) \ ++ xprintk(dev , KERN_WARNING , fmt , ## args) ++#define INFO(dev,fmt,args...) \ ++ xprintk(dev , KERN_INFO , fmt , ## args) ++ ++/*-------------------------------------------------------------------------*/ ++ ++static unsigned buflen = 4096; ++static unsigned qlen = 32; ++static unsigned pattern = 0; ++ ++module_param (buflen, uint, S_IRUGO|S_IWUSR); ++module_param (qlen, uint, S_IRUGO|S_IWUSR); ++module_param (pattern, uint, S_IRUGO|S_IWUSR); ++ ++/* ++ * if it's nonzero, autoresume says how many seconds to wait ++ * before trying to wake up the host after suspend. ++ */ ++static unsigned autoresume = 0; ++module_param (autoresume, uint, 0); ++ ++/* ++ * Normally the "loopback" configuration is second (index 1) so ++ * it's not the default. Here's where to change that order, to ++ * work better with hosts where config changes are problematic. ++ * Or controllers (like superh) that only support one config. ++ */ ++static int loopdefault = 0; ++ ++module_param (loopdefault, bool, S_IRUGO|S_IWUSR); ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* Thanks to NetChip Technologies for donating this product ID. ++ * ++ * DO NOT REUSE THESE IDs with a protocol-incompatible driver!! Ever!! ++ * Instead: allocate your own, using normal USB-IF procedures. ++ */ ++#ifndef CONFIG_USB_ZERO_HNPTEST ++#define DRIVER_VENDOR_NUM 0x0525 /* NetChip */ ++#define DRIVER_PRODUCT_NUM 0xa4a0 /* Linux-USB "Gadget Zero" */ ++#else ++#define DRIVER_VENDOR_NUM 0x1a0a /* OTG test device IDs */ ++#define DRIVER_PRODUCT_NUM 0xbadd ++#endif ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* ++ * DESCRIPTORS ... most are static, but strings and (full) ++ * configuration descriptors are built on demand. ++ */ ++ ++/* ++#define STRING_MANUFACTURER 25 ++#define STRING_PRODUCT 42 ++#define STRING_SERIAL 101 ++*/ ++#define STRING_MANUFACTURER 1 ++#define STRING_PRODUCT 2 ++#define STRING_SERIAL 3 ++ ++#define STRING_SOURCE_SINK 250 ++#define STRING_LOOPBACK 251 ++ ++/* ++ * This device advertises two configurations; these numbers work ++ * on a pxa250 as well as more flexible hardware. ++ */ ++#define CONFIG_SOURCE_SINK 3 ++#define CONFIG_LOOPBACK 2 ++ ++/* ++static struct usb_device_descriptor ++device_desc = { ++ .bLength = sizeof device_desc, ++ .bDescriptorType = USB_DT_DEVICE, ++ ++ .bcdUSB = __constant_cpu_to_le16 (0x0200), ++ .bDeviceClass = USB_CLASS_VENDOR_SPEC, ++ ++ .idVendor = __constant_cpu_to_le16 (DRIVER_VENDOR_NUM), ++ .idProduct = __constant_cpu_to_le16 (DRIVER_PRODUCT_NUM), ++ .iManufacturer = STRING_MANUFACTURER, ++ .iProduct = STRING_PRODUCT, ++ .iSerialNumber = STRING_SERIAL, ++ .bNumConfigurations = 2, ++}; ++*/ ++static struct usb_device_descriptor ++device_desc = { ++ .bLength = sizeof device_desc, ++ .bDescriptorType = USB_DT_DEVICE, ++ .bcdUSB = __constant_cpu_to_le16 (0x0100), ++ .bDeviceClass = USB_CLASS_PER_INTERFACE, ++ .bDeviceSubClass = 0, ++ .bDeviceProtocol = 0, ++ .bMaxPacketSize0 = 64, ++ .bcdDevice = __constant_cpu_to_le16 (0x0100), ++ .idVendor = __constant_cpu_to_le16 (0x0499), ++ .idProduct = __constant_cpu_to_le16 (0x3002), ++ .iManufacturer = STRING_MANUFACTURER, ++ .iProduct = STRING_PRODUCT, ++ .iSerialNumber = STRING_SERIAL, ++ .bNumConfigurations = 1, ++}; ++ ++static struct usb_config_descriptor ++z_config = { ++ .bLength = sizeof z_config, ++ .bDescriptorType = USB_DT_CONFIG, ++ ++ /* compute wTotalLength on the fly */ ++ .bNumInterfaces = 2, ++ .bConfigurationValue = 1, ++ .iConfiguration = 0, ++ .bmAttributes = 0x40, ++ .bMaxPower = 0, /* self-powered */ ++}; ++ ++ ++static struct usb_otg_descriptor ++otg_descriptor = { ++ .bLength = sizeof otg_descriptor, ++ .bDescriptorType = USB_DT_OTG, ++ ++ .bmAttributes = USB_OTG_SRP, ++}; ++ ++/* one interface in each configuration */ ++#ifdef CONFIG_USB_GADGET_DUALSPEED ++ ++/* ++ * usb 2.0 devices need to expose both high speed and full speed ++ * descriptors, unless they only run at full speed. ++ * ++ * that means alternate endpoint descriptors (bigger packets) ++ * and a "device qualifier" ... plus more construction options ++ * for the config descriptor. ++ */ ++ ++static struct usb_qualifier_descriptor ++dev_qualifier = { ++ .bLength = sizeof dev_qualifier, ++ .bDescriptorType = USB_DT_DEVICE_QUALIFIER, ++ ++ .bcdUSB = __constant_cpu_to_le16 (0x0200), ++ .bDeviceClass = USB_CLASS_VENDOR_SPEC, ++ ++ .bNumConfigurations = 2, ++}; ++ ++ ++struct usb_cs_as_general_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __u8 bDescriptorSubType; ++ __u8 bTerminalLink; ++ __u8 bDelay; ++ __u16 wFormatTag; ++} __attribute__ ((packed)); ++ ++struct usb_cs_as_format_descriptor { ++ __u8 bLength; ++ __u8 bDescriptorType; ++ ++ __u8 bDescriptorSubType; ++ __u8 bFormatType; ++ __u8 bNrChannels; ++ __u8 bSubframeSize; ++ __u8 bBitResolution; ++ __u8 bSamfreqType; ++ __u8 tLowerSamFreq[3]; ++ __u8 tUpperSamFreq[3]; ++} __attribute__ ((packed)); ++ ++static const struct usb_interface_descriptor ++z_audio_control_if_desc = { ++ .bLength = sizeof z_audio_control_if_desc, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 0, ++ .bAlternateSetting = 0, ++ .bNumEndpoints = 0, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = 0x1, ++ .bInterfaceProtocol = 0, ++ .iInterface = 0, ++}; ++ ++static const struct usb_interface_descriptor ++z_audio_if_desc = { ++ .bLength = sizeof z_audio_if_desc, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 1, ++ .bAlternateSetting = 0, ++ .bNumEndpoints = 0, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = 0x2, ++ .bInterfaceProtocol = 0, ++ .iInterface = 0, ++}; ++ ++static const struct usb_interface_descriptor ++z_audio_if_desc2 = { ++ .bLength = sizeof z_audio_if_desc, ++ .bDescriptorType = USB_DT_INTERFACE, ++ .bInterfaceNumber = 1, ++ .bAlternateSetting = 1, ++ .bNumEndpoints = 1, ++ .bInterfaceClass = USB_CLASS_AUDIO, ++ .bInterfaceSubClass = 0x2, ++ .bInterfaceProtocol = 0, ++ .iInterface = 0, ++}; ++ ++static const struct usb_cs_as_general_descriptor ++z_audio_cs_as_if_desc = { ++ .bLength = 7, ++ .bDescriptorType = 0x24, ++ ++ .bDescriptorSubType = 0x01, ++ .bTerminalLink = 0x01, ++ .bDelay = 0x0, ++ .wFormatTag = __constant_cpu_to_le16 (0x0001) ++}; ++ ++ ++static const struct usb_cs_as_format_descriptor ++z_audio_cs_as_format_desc = { ++ .bLength = 0xe, ++ .bDescriptorType = 0x24, ++ ++ .bDescriptorSubType = 2, ++ .bFormatType = 1, ++ .bNrChannels = 1, ++ .bSubframeSize = 1, ++ .bBitResolution = 8, ++ .bSamfreqType = 0, ++ .tLowerSamFreq = {0x7e, 0x13, 0x00}, ++ .tUpperSamFreq = {0xe2, 0xd6, 0x00}, ++}; ++ ++static const struct usb_endpoint_descriptor ++z_iso_ep = { ++ .bLength = 0x09, ++ .bDescriptorType = 0x05, ++ .bEndpointAddress = 0x04, ++ .bmAttributes = 0x09, ++ .wMaxPacketSize = 0x0038, ++ .bInterval = 0x01, ++ .bRefresh = 0x00, ++ .bSynchAddress = 0x00, ++}; ++ ++static char z_iso_ep2[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02}; ++ ++// 9 bytes ++static char z_ac_interface_header_desc[] = ++{ 0x09, 0x24, 0x01, 0x00, 0x01, 0x2b, 0x00, 0x01, 0x01 }; ++ ++// 12 bytes ++static char z_0[] = {0x0c, 0x24, 0x02, 0x01, 0x01, 0x01, 0x00, 0x02, ++ 0x03, 0x00, 0x00, 0x00}; ++// 13 bytes ++static char z_1[] = {0x0d, 0x24, 0x06, 0x02, 0x01, 0x02, 0x15, 0x00, ++ 0x02, 0x00, 0x02, 0x00, 0x00}; ++// 9 bytes ++static char z_2[] = {0x09, 0x24, 0x03, 0x03, 0x01, 0x03, 0x00, 0x02, ++ 0x00}; ++ ++static char za_0[] = {0x09, 0x04, 0x01, 0x02, 0x01, 0x01, 0x02, 0x00, ++ 0x00}; ++ ++static char za_1[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00}; ++ ++static char za_2[] = {0x0e, 0x24, 0x02, 0x01, 0x02, 0x01, 0x08, 0x00, ++ 0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00}; ++ ++static char za_3[] = {0x09, 0x05, 0x04, 0x09, 0x70, 0x00, 0x01, 0x00, ++ 0x00}; ++ ++static char za_4[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02}; ++ ++static char za_5[] = {0x09, 0x04, 0x01, 0x03, 0x01, 0x01, 0x02, 0x00, ++ 0x00}; ++ ++static char za_6[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00}; ++ ++static char za_7[] = {0x0e, 0x24, 0x02, 0x01, 0x01, 0x02, 0x10, 0x00, ++ 0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00}; ++ ++static char za_8[] = {0x09, 0x05, 0x04, 0x09, 0x70, 0x00, 0x01, 0x00, ++ 0x00}; ++ ++static char za_9[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02}; ++ ++static char za_10[] = {0x09, 0x04, 0x01, 0x04, 0x01, 0x01, 0x02, 0x00, ++ 0x00}; ++ ++static char za_11[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00}; ++ ++static char za_12[] = {0x0e, 0x24, 0x02, 0x01, 0x02, 0x02, 0x10, 0x00, ++ 0x73, 0x13, 0x00, 0xe2, 0xd6, 0x00}; ++ ++static char za_13[] = {0x09, 0x05, 0x04, 0x09, 0xe0, 0x00, 0x01, 0x00, ++ 0x00}; ++ ++static char za_14[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02}; ++ ++static char za_15[] = {0x09, 0x04, 0x01, 0x05, 0x01, 0x01, 0x02, 0x00, ++ 0x00}; ++ ++static char za_16[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00}; ++ ++static char za_17[] = {0x0e, 0x24, 0x02, 0x01, 0x01, 0x03, 0x14, 0x00, ++ 0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00}; ++ ++static char za_18[] = {0x09, 0x05, 0x04, 0x09, 0xa8, 0x00, 0x01, 0x00, ++ 0x00}; ++ ++static char za_19[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02}; ++ ++static char za_20[] = {0x09, 0x04, 0x01, 0x06, 0x01, 0x01, 0x02, 0x00, ++ 0x00}; ++ ++static char za_21[] = {0x07, 0x24, 0x01, 0x01, 0x00, 0x01, 0x00}; ++ ++static char za_22[] = {0x0e, 0x24, 0x02, 0x01, 0x02, 0x03, 0x14, 0x00, ++ 0x7e, 0x13, 0x00, 0xe2, 0xd6, 0x00}; ++ ++static char za_23[] = {0x09, 0x05, 0x04, 0x09, 0x50, 0x01, 0x01, 0x00, ++ 0x00}; ++ ++static char za_24[] = {0x07, 0x25, 0x01, 0x00, 0x02, 0x00, 0x02}; ++ ++ ++ ++static const struct usb_descriptor_header *z_function [] = { ++ (struct usb_descriptor_header *) &z_audio_control_if_desc, ++ (struct usb_descriptor_header *) &z_ac_interface_header_desc, ++ (struct usb_descriptor_header *) &z_0, ++ (struct usb_descriptor_header *) &z_1, ++ (struct usb_descriptor_header *) &z_2, ++ (struct usb_descriptor_header *) &z_audio_if_desc, ++ (struct usb_descriptor_header *) &z_audio_if_desc2, ++ (struct usb_descriptor_header *) &z_audio_cs_as_if_desc, ++ (struct usb_descriptor_header *) &z_audio_cs_as_format_desc, ++ (struct usb_descriptor_header *) &z_iso_ep, ++ (struct usb_descriptor_header *) &z_iso_ep2, ++ (struct usb_descriptor_header *) &za_0, ++ (struct usb_descriptor_header *) &za_1, ++ (struct usb_descriptor_header *) &za_2, ++ (struct usb_descriptor_header *) &za_3, ++ (struct usb_descriptor_header *) &za_4, ++ (struct usb_descriptor_header *) &za_5, ++ (struct usb_descriptor_header *) &za_6, ++ (struct usb_descriptor_header *) &za_7, ++ (struct usb_descriptor_header *) &za_8, ++ (struct usb_descriptor_header *) &za_9, ++ (struct usb_descriptor_header *) &za_10, ++ (struct usb_descriptor_header *) &za_11, ++ (struct usb_descriptor_header *) &za_12, ++ (struct usb_descriptor_header *) &za_13, ++ (struct usb_descriptor_header *) &za_14, ++ (struct usb_descriptor_header *) &za_15, ++ (struct usb_descriptor_header *) &za_16, ++ (struct usb_descriptor_header *) &za_17, ++ (struct usb_descriptor_header *) &za_18, ++ (struct usb_descriptor_header *) &za_19, ++ (struct usb_descriptor_header *) &za_20, ++ (struct usb_descriptor_header *) &za_21, ++ (struct usb_descriptor_header *) &za_22, ++ (struct usb_descriptor_header *) &za_23, ++ (struct usb_descriptor_header *) &za_24, ++ NULL, ++}; ++ ++/* maxpacket and other transfer characteristics vary by speed. */ ++#define ep_desc(g,hs,fs) (((g)->speed==USB_SPEED_HIGH)?(hs):(fs)) ++ ++#else ++ ++/* if there's no high speed support, maxpacket doesn't change. */ ++#define ep_desc(g,hs,fs) fs ++ ++#endif /* !CONFIG_USB_GADGET_DUALSPEED */ ++ ++static char manufacturer [40]; ++//static char serial [40]; ++static char serial [] = "Ser 00 em"; ++ ++/* static strings, in UTF-8 */ ++static struct usb_string strings [] = { ++ { STRING_MANUFACTURER, manufacturer, }, ++ { STRING_PRODUCT, longname, }, ++ { STRING_SERIAL, serial, }, ++ { STRING_LOOPBACK, loopback, }, ++ { STRING_SOURCE_SINK, source_sink, }, ++ { } /* end of list */ ++}; ++ ++static struct usb_gadget_strings stringtab = { ++ .language = 0x0409, /* en-us */ ++ .strings = strings, ++}; ++ ++/* ++ * config descriptors are also handcrafted. these must agree with code ++ * that sets configurations, and with code managing interfaces and their ++ * altsettings. other complexity may come from: ++ * ++ * - high speed support, including "other speed config" rules ++ * - multiple configurations ++ * - interfaces with alternate settings ++ * - embedded class or vendor-specific descriptors ++ * ++ * this handles high speed, and has a second config that could as easily ++ * have been an alternate interface setting (on most hardware). ++ * ++ * NOTE: to demonstrate (and test) more USB capabilities, this driver ++ * should include an altsetting to test interrupt transfers, including ++ * high bandwidth modes at high speed. (Maybe work like Intel's test ++ * device?) ++ */ ++static int ++config_buf (struct usb_gadget *gadget, u8 *buf, u8 type, unsigned index) ++{ ++ int len; ++ const struct usb_descriptor_header **function; ++ ++ function = z_function; ++ len = usb_gadget_config_buf (&z_config, buf, USB_BUFSIZ, function); ++ if (len < 0) ++ return len; ++ ((struct usb_config_descriptor *) buf)->bDescriptorType = type; ++ return len; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_request * ++alloc_ep_req (struct usb_ep *ep, unsigned length) ++{ ++ struct usb_request *req; ++ ++ req = usb_ep_alloc_request (ep, GFP_ATOMIC); ++ if (req) { ++ req->length = length; ++ req->buf = usb_ep_alloc_buffer (ep, length, ++ &req->dma, GFP_ATOMIC); ++ if (!req->buf) { ++ usb_ep_free_request (ep, req); ++ req = NULL; ++ } ++ } ++ return req; ++} ++ ++static void free_ep_req (struct usb_ep *ep, struct usb_request *req) ++{ ++ if (req->buf) ++ usb_ep_free_buffer (ep, req->buf, req->dma, req->length); ++ usb_ep_free_request (ep, req); ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++/* optionally require specific source/sink data patterns */ ++ ++static int ++check_read_data ( ++ struct zero_dev *dev, ++ struct usb_ep *ep, ++ struct usb_request *req ++) ++{ ++ unsigned i; ++ u8 *buf = req->buf; ++ ++ for (i = 0; i < req->actual; i++, buf++) { ++ switch (pattern) { ++ /* all-zeroes has no synchronization issues */ ++ case 0: ++ if (*buf == 0) ++ continue; ++ break; ++ /* mod63 stays in sync with short-terminated transfers, ++ * or otherwise when host and gadget agree on how large ++ * each usb transfer request should be. resync is done ++ * with set_interface or set_config. ++ */ ++ case 1: ++ if (*buf == (u8)(i % 63)) ++ continue; ++ break; ++ } ++ ERROR (dev, "bad OUT byte, buf [%d] = %d\n", i, *buf); ++ usb_ep_set_halt (ep); ++ return -EINVAL; ++ } ++ return 0; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void zero_reset_config (struct zero_dev *dev) ++{ ++ if (dev->config == 0) ++ return; ++ ++ DBG (dev, "reset config\n"); ++ ++ /* just disable endpoints, forcing completion of pending i/o. ++ * all our completion handlers free their requests in this case. ++ */ ++ if (dev->in_ep) { ++ usb_ep_disable (dev->in_ep); ++ dev->in_ep = NULL; ++ } ++ if (dev->out_ep) { ++ usb_ep_disable (dev->out_ep); ++ dev->out_ep = NULL; ++ } ++ dev->config = 0; ++ del_timer (&dev->resume); ++} ++ ++#define _write(f, buf, sz) (f->f_op->write(f, buf, sz, &f->f_pos)) ++ ++static void ++zero_isoc_complete (struct usb_ep *ep, struct usb_request *req) ++{ ++ struct zero_dev *dev = ep->driver_data; ++ int status = req->status; ++ int i, j; ++ ++ switch (status) { ++ ++ case 0: /* normal completion? */ ++ //printk ("\nzero ---------------> isoc normal completion %d bytes\n", req->actual); ++ for (i=0, j=rbuf_start; i<req->actual; i++) { ++ //printk ("%02x ", ((__u8*)req->buf)[i]); ++ rbuf[j] = ((__u8*)req->buf)[i]; ++ j++; ++ if (j >= RBUF_LEN) j=0; ++ } ++ rbuf_start = j; ++ //printk ("\n\n"); ++ ++ if (rbuf_len < RBUF_LEN) { ++ rbuf_len += req->actual; ++ if (rbuf_len > RBUF_LEN) { ++ rbuf_len = RBUF_LEN; ++ } ++ } ++ ++ break; ++ ++ /* this endpoint is normally active while we're configured */ ++ case -ECONNABORTED: /* hardware forced ep reset */ ++ case -ECONNRESET: /* request dequeued */ ++ case -ESHUTDOWN: /* disconnect from host */ ++ VDBG (dev, "%s gone (%d), %d/%d\n", ep->name, status, ++ req->actual, req->length); ++ if (ep == dev->out_ep) ++ check_read_data (dev, ep, req); ++ free_ep_req (ep, req); ++ return; ++ ++ case -EOVERFLOW: /* buffer overrun on read means that ++ * we didn't provide a big enough ++ * buffer. ++ */ ++ default: ++#if 1 ++ DBG (dev, "%s complete --> %d, %d/%d\n", ep->name, ++ status, req->actual, req->length); ++#endif ++ case -EREMOTEIO: /* short read */ ++ break; ++ } ++ ++ status = usb_ep_queue (ep, req, GFP_ATOMIC); ++ if (status) { ++ ERROR (dev, "kill %s: resubmit %d bytes --> %d\n", ++ ep->name, req->length, status); ++ usb_ep_set_halt (ep); ++ /* FIXME recover later ... somehow */ ++ } ++} ++ ++static struct usb_request * ++zero_start_isoc_ep (struct usb_ep *ep, int gfp_flags) ++{ ++ struct usb_request *req; ++ int status; ++ ++ req = alloc_ep_req (ep, 512); ++ if (!req) ++ return NULL; ++ ++ req->complete = zero_isoc_complete; ++ ++ status = usb_ep_queue (ep, req, gfp_flags); ++ if (status) { ++ struct zero_dev *dev = ep->driver_data; ++ ++ ERROR (dev, "start %s --> %d\n", ep->name, status); ++ free_ep_req (ep, req); ++ req = NULL; ++ } ++ ++ return req; ++} ++ ++/* change our operational config. this code must agree with the code ++ * that returns config descriptors, and altsetting code. ++ * ++ * it's also responsible for power management interactions. some ++ * configurations might not work with our current power sources. ++ * ++ * note that some device controller hardware will constrain what this ++ * code can do, perhaps by disallowing more than one configuration or ++ * by limiting configuration choices (like the pxa2xx). ++ */ ++static int ++zero_set_config (struct zero_dev *dev, unsigned number, int gfp_flags) ++{ ++ int result = 0; ++ struct usb_gadget *gadget = dev->gadget; ++ const struct usb_endpoint_descriptor *d; ++ struct usb_ep *ep; ++ ++ if (number == dev->config) ++ return 0; ++ ++ zero_reset_config (dev); ++ ++ gadget_for_each_ep (ep, gadget) { ++ ++ if (strcmp (ep->name, "ep4") == 0) { ++ ++ d = (struct usb_endpoint_descripter *)&za_23; // isoc ep desc for audio i/f alt setting 6 ++ result = usb_ep_enable (ep, d); ++ ++ if (result == 0) { ++ ep->driver_data = dev; ++ dev->in_ep = ep; ++ ++ if (zero_start_isoc_ep (ep, gfp_flags) != 0) { ++ ++ dev->in_ep = ep; ++ continue; ++ } ++ ++ usb_ep_disable (ep); ++ result = -EIO; ++ } ++ } ++ ++ } ++ ++ dev->config = number; ++ return result; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void zero_setup_complete (struct usb_ep *ep, struct usb_request *req) ++{ ++ if (req->status || req->actual != req->length) ++ DBG ((struct zero_dev *) ep->driver_data, ++ "setup complete --> %d, %d/%d\n", ++ req->status, req->actual, req->length); ++} ++ ++/* ++ * The setup() callback implements all the ep0 functionality that's ++ * not handled lower down, in hardware or the hardware driver (like ++ * device and endpoint feature flags, and their status). It's all ++ * housekeeping for the gadget function we're implementing. Most of ++ * the work is in config-specific setup. ++ */ ++static int ++zero_setup (struct usb_gadget *gadget, const struct usb_ctrlrequest *ctrl) ++{ ++ struct zero_dev *dev = get_gadget_data (gadget); ++ struct usb_request *req = dev->req; ++ int value = -EOPNOTSUPP; ++ ++ /* usually this stores reply data in the pre-allocated ep0 buffer, ++ * but config change events will reconfigure hardware. ++ */ ++ req->zero = 0; ++ switch (ctrl->bRequest) { ++ ++ case USB_REQ_GET_DESCRIPTOR: ++ ++ switch (ctrl->wValue >> 8) { ++ ++ case USB_DT_DEVICE: ++ value = min (ctrl->wLength, (u16) sizeof device_desc); ++ memcpy (req->buf, &device_desc, value); ++ break; ++#ifdef CONFIG_USB_GADGET_DUALSPEED ++ case USB_DT_DEVICE_QUALIFIER: ++ if (!gadget->is_dualspeed) ++ break; ++ value = min (ctrl->wLength, (u16) sizeof dev_qualifier); ++ memcpy (req->buf, &dev_qualifier, value); ++ break; ++ ++ case USB_DT_OTHER_SPEED_CONFIG: ++ if (!gadget->is_dualspeed) ++ break; ++ // FALLTHROUGH ++#endif /* CONFIG_USB_GADGET_DUALSPEED */ ++ case USB_DT_CONFIG: ++ value = config_buf (gadget, req->buf, ++ ctrl->wValue >> 8, ++ ctrl->wValue & 0xff); ++ if (value >= 0) ++ value = min (ctrl->wLength, (u16) value); ++ break; ++ ++ case USB_DT_STRING: ++ /* wIndex == language code. ++ * this driver only handles one language, you can ++ * add string tables for other languages, using ++ * any UTF-8 characters ++ */ ++ value = usb_gadget_get_string (&stringtab, ++ ctrl->wValue & 0xff, req->buf); ++ if (value >= 0) { ++ value = min (ctrl->wLength, (u16) value); ++ } ++ break; ++ } ++ break; ++ ++ /* currently two configs, two speeds */ ++ case USB_REQ_SET_CONFIGURATION: ++ if (ctrl->bRequestType != 0) ++ goto unknown; ++ ++ spin_lock (&dev->lock); ++ value = zero_set_config (dev, ctrl->wValue, GFP_ATOMIC); ++ spin_unlock (&dev->lock); ++ break; ++ case USB_REQ_GET_CONFIGURATION: ++ if (ctrl->bRequestType != USB_DIR_IN) ++ goto unknown; ++ *(u8 *)req->buf = dev->config; ++ value = min (ctrl->wLength, (u16) 1); ++ break; ++ ++ /* until we add altsetting support, or other interfaces, ++ * only 0/0 are possible. pxa2xx only supports 0/0 (poorly) ++ * and already killed pending endpoint I/O. ++ */ ++ case USB_REQ_SET_INTERFACE: ++ ++ if (ctrl->bRequestType != USB_RECIP_INTERFACE) ++ goto unknown; ++ spin_lock (&dev->lock); ++ if (dev->config) { ++ u8 config = dev->config; ++ ++ /* resets interface configuration, forgets about ++ * previous transaction state (queued bufs, etc) ++ * and re-inits endpoint state (toggle etc) ++ * no response queued, just zero status == success. ++ * if we had more than one interface we couldn't ++ * use this "reset the config" shortcut. ++ */ ++ zero_reset_config (dev); ++ zero_set_config (dev, config, GFP_ATOMIC); ++ value = 0; ++ } ++ spin_unlock (&dev->lock); ++ break; ++ case USB_REQ_GET_INTERFACE: ++ if ((ctrl->bRequestType == 0x21) && (ctrl->wIndex == 0x02)) { ++ value = ctrl->wLength; ++ break; ++ } ++ else { ++ if (ctrl->bRequestType != (USB_DIR_IN|USB_RECIP_INTERFACE)) ++ goto unknown; ++ if (!dev->config) ++ break; ++ if (ctrl->wIndex != 0) { ++ value = -EDOM; ++ break; ++ } ++ *(u8 *)req->buf = 0; ++ value = min (ctrl->wLength, (u16) 1); ++ } ++ break; ++ ++ /* ++ * These are the same vendor-specific requests supported by ++ * Intel's USB 2.0 compliance test devices. We exceed that ++ * device spec by allowing multiple-packet requests. ++ */ ++ case 0x5b: /* control WRITE test -- fill the buffer */ ++ if (ctrl->bRequestType != (USB_DIR_OUT|USB_TYPE_VENDOR)) ++ goto unknown; ++ if (ctrl->wValue || ctrl->wIndex) ++ break; ++ /* just read that many bytes into the buffer */ ++ if (ctrl->wLength > USB_BUFSIZ) ++ break; ++ value = ctrl->wLength; ++ break; ++ case 0x5c: /* control READ test -- return the buffer */ ++ if (ctrl->bRequestType != (USB_DIR_IN|USB_TYPE_VENDOR)) ++ goto unknown; ++ if (ctrl->wValue || ctrl->wIndex) ++ break; ++ /* expect those bytes are still in the buffer; send back */ ++ if (ctrl->wLength > USB_BUFSIZ ++ || ctrl->wLength != req->length) ++ break; ++ value = ctrl->wLength; ++ break; ++ ++ case 0x01: // SET_CUR ++ case 0x02: ++ case 0x03: ++ case 0x04: ++ case 0x05: ++ value = ctrl->wLength; ++ break; ++ case 0x81: ++ switch (ctrl->wValue) { ++ case 0x0201: ++ case 0x0202: ++ ((u8*)req->buf)[0] = 0x00; ++ ((u8*)req->buf)[1] = 0xe3; ++ break; ++ case 0x0300: ++ case 0x0500: ++ ((u8*)req->buf)[0] = 0x00; ++ break; ++ } ++ //((u8*)req->buf)[0] = 0x81; ++ //((u8*)req->buf)[1] = 0x81; ++ value = ctrl->wLength; ++ break; ++ case 0x82: ++ switch (ctrl->wValue) { ++ case 0x0201: ++ case 0x0202: ++ ((u8*)req->buf)[0] = 0x00; ++ ((u8*)req->buf)[1] = 0xc3; ++ break; ++ case 0x0300: ++ case 0x0500: ++ ((u8*)req->buf)[0] = 0x00; ++ break; ++ } ++ //((u8*)req->buf)[0] = 0x82; ++ //((u8*)req->buf)[1] = 0x82; ++ value = ctrl->wLength; ++ break; ++ case 0x83: ++ switch (ctrl->wValue) { ++ case 0x0201: ++ case 0x0202: ++ ((u8*)req->buf)[0] = 0x00; ++ ((u8*)req->buf)[1] = 0x00; ++ break; ++ case 0x0300: ++ ((u8*)req->buf)[0] = 0x60; ++ break; ++ case 0x0500: ++ ((u8*)req->buf)[0] = 0x18; ++ break; ++ } ++ //((u8*)req->buf)[0] = 0x83; ++ //((u8*)req->buf)[1] = 0x83; ++ value = ctrl->wLength; ++ break; ++ case 0x84: ++ switch (ctrl->wValue) { ++ case 0x0201: ++ case 0x0202: ++ ((u8*)req->buf)[0] = 0x00; ++ ((u8*)req->buf)[1] = 0x01; ++ break; ++ case 0x0300: ++ case 0x0500: ++ ((u8*)req->buf)[0] = 0x08; ++ break; ++ } ++ //((u8*)req->buf)[0] = 0x84; ++ //((u8*)req->buf)[1] = 0x84; ++ value = ctrl->wLength; ++ break; ++ case 0x85: ++ ((u8*)req->buf)[0] = 0x85; ++ ((u8*)req->buf)[1] = 0x85; ++ value = ctrl->wLength; ++ break; ++ ++ ++ default: ++unknown: ++ printk("unknown control req%02x.%02x v%04x i%04x l%d\n", ++ ctrl->bRequestType, ctrl->bRequest, ++ ctrl->wValue, ctrl->wIndex, ctrl->wLength); ++ } ++ ++ /* respond with data transfer before status phase? */ ++ if (value >= 0) { ++ req->length = value; ++ req->zero = value < ctrl->wLength ++ && (value % gadget->ep0->maxpacket) == 0; ++ value = usb_ep_queue (gadget->ep0, req, GFP_ATOMIC); ++ if (value < 0) { ++ DBG (dev, "ep_queue < 0 --> %d\n", value); ++ req->status = 0; ++ zero_setup_complete (gadget->ep0, req); ++ } ++ } ++ ++ /* device either stalls (value < 0) or reports success */ ++ return value; ++} ++ ++static void ++zero_disconnect (struct usb_gadget *gadget) ++{ ++ struct zero_dev *dev = get_gadget_data (gadget); ++ unsigned long flags; ++ ++ spin_lock_irqsave (&dev->lock, flags); ++ zero_reset_config (dev); ++ ++ /* a more significant application might have some non-usb ++ * activities to quiesce here, saving resources like power ++ * or pushing the notification up a network stack. ++ */ ++ spin_unlock_irqrestore (&dev->lock, flags); ++ ++ /* next we may get setup() calls to enumerate new connections; ++ * or an unbind() during shutdown (including removing module). ++ */ ++} ++ ++static void ++zero_autoresume (unsigned long _dev) ++{ ++ struct zero_dev *dev = (struct zero_dev *) _dev; ++ int status; ++ ++ /* normally the host would be woken up for something ++ * more significant than just a timer firing... ++ */ ++ if (dev->gadget->speed != USB_SPEED_UNKNOWN) { ++ status = usb_gadget_wakeup (dev->gadget); ++ DBG (dev, "wakeup --> %d\n", status); ++ } ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void ++zero_unbind (struct usb_gadget *gadget) ++{ ++ struct zero_dev *dev = get_gadget_data (gadget); ++ ++ DBG (dev, "unbind\n"); ++ ++ /* we've already been disconnected ... no i/o is active */ ++ if (dev->req) ++ free_ep_req (gadget->ep0, dev->req); ++ del_timer_sync (&dev->resume); ++ kfree (dev); ++ set_gadget_data (gadget, NULL); ++} ++ ++static int ++zero_bind (struct usb_gadget *gadget) ++{ ++ struct zero_dev *dev; ++ //struct usb_ep *ep; ++ ++ printk("binding\n"); ++ /* ++ * DRIVER POLICY CHOICE: you may want to do this differently. ++ * One thing to avoid is reusing a bcdDevice revision code ++ * with different host-visible configurations or behavior ++ * restrictions -- using ep1in/ep2out vs ep1out/ep3in, etc ++ */ ++ //device_desc.bcdDevice = __constant_cpu_to_le16 (0x0201); ++ ++ ++ /* ok, we made sense of the hardware ... */ ++ dev = kmalloc (sizeof *dev, SLAB_KERNEL); ++ if (!dev) ++ return -ENOMEM; ++ memset (dev, 0, sizeof *dev); ++ spin_lock_init (&dev->lock); ++ dev->gadget = gadget; ++ set_gadget_data (gadget, dev); ++ ++ /* preallocate control response and buffer */ ++ dev->req = usb_ep_alloc_request (gadget->ep0, GFP_KERNEL); ++ if (!dev->req) ++ goto enomem; ++ dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ, ++ &dev->req->dma, GFP_KERNEL); ++ if (!dev->req->buf) ++ goto enomem; ++ ++ dev->req->complete = zero_setup_complete; ++ ++ device_desc.bMaxPacketSize0 = gadget->ep0->maxpacket; ++ ++#ifdef CONFIG_USB_GADGET_DUALSPEED ++ /* assume ep0 uses the same value for both speeds ... */ ++ dev_qualifier.bMaxPacketSize0 = device_desc.bMaxPacketSize0; ++ ++ /* and that all endpoints are dual-speed */ ++ //hs_source_desc.bEndpointAddress = fs_source_desc.bEndpointAddress; ++ //hs_sink_desc.bEndpointAddress = fs_sink_desc.bEndpointAddress; ++#endif ++ ++ usb_gadget_set_selfpowered (gadget); ++ ++ init_timer (&dev->resume); ++ dev->resume.function = zero_autoresume; ++ dev->resume.data = (unsigned long) dev; ++ ++ gadget->ep0->driver_data = dev; ++ ++ INFO (dev, "%s, version: " DRIVER_VERSION "\n", longname); ++ INFO (dev, "using %s, OUT %s IN %s\n", gadget->name, ++ EP_OUT_NAME, EP_IN_NAME); ++ ++ snprintf (manufacturer, sizeof manufacturer, ++ UTS_SYSNAME " " UTS_RELEASE " with %s", ++ gadget->name); ++ ++ return 0; ++ ++enomem: ++ zero_unbind (gadget); ++ return -ENOMEM; ++} ++ ++/*-------------------------------------------------------------------------*/ ++ ++static void ++zero_suspend (struct usb_gadget *gadget) ++{ ++ struct zero_dev *dev = get_gadget_data (gadget); ++ ++ if (gadget->speed == USB_SPEED_UNKNOWN) ++ return; ++ ++ if (autoresume) { ++ mod_timer (&dev->resume, jiffies + (HZ * autoresume)); ++ DBG (dev, "suspend, wakeup in %d seconds\n", autoresume); ++ } else ++ DBG (dev, "suspend\n"); ++} ++ ++static void ++zero_resume (struct usb_gadget *gadget) ++{ ++ struct zero_dev *dev = get_gadget_data (gadget); ++ ++ DBG (dev, "resume\n"); ++ del_timer (&dev->resume); ++} ++ ++ ++/*-------------------------------------------------------------------------*/ ++ ++static struct usb_gadget_driver zero_driver = { ++#ifdef CONFIG_USB_GADGET_DUALSPEED ++ .speed = USB_SPEED_HIGH, ++#else ++ .speed = USB_SPEED_FULL, ++#endif ++ .function = (char *) longname, ++ .bind = zero_bind, ++ .unbind = zero_unbind, ++ ++ .setup = zero_setup, ++ .disconnect = zero_disconnect, ++ ++ .suspend = zero_suspend, ++ .resume = zero_resume, ++ ++ .driver = { ++ .name = (char *) shortname, ++ // .shutdown = ... ++ // .suspend = ... ++ // .resume = ... ++ }, ++}; ++ ++MODULE_AUTHOR ("David Brownell"); ++MODULE_LICENSE ("Dual BSD/GPL"); ++ ++static struct proc_dir_entry *pdir, *pfile; ++ ++static int isoc_read_data (char *page, char **start, ++ off_t off, int count, ++ int *eof, void *data) ++{ ++ int i; ++ static int c = 0; ++ static int done = 0; ++ static int s = 0; ++ ++/* ++ printk ("\ncount: %d\n", count); ++ printk ("rbuf_start: %d\n", rbuf_start); ++ printk ("rbuf_len: %d\n", rbuf_len); ++ printk ("off: %d\n", off); ++ printk ("start: %p\n\n", *start); ++*/ ++ if (done) { ++ c = 0; ++ done = 0; ++ *eof = 1; ++ return 0; ++ } ++ ++ if (c == 0) { ++ if (rbuf_len == RBUF_LEN) ++ s = rbuf_start; ++ else s = 0; ++ } ++ ++ for (i=0; i<count && c<rbuf_len; i++, c++) { ++ page[i] = rbuf[(c+s) % RBUF_LEN]; ++ } ++ *start = page; ++ ++ if (c >= rbuf_len) { ++ *eof = 1; ++ done = 1; ++ } ++ ++ ++ return i; ++} ++ ++static int __init init (void) ++{ ++ ++ int retval = 0; ++ ++ pdir = proc_mkdir("isoc_test", NULL); ++ if(pdir == NULL) { ++ retval = -ENOMEM; ++ printk("Error creating dir\n"); ++ goto done; ++ } ++ pdir->owner = THIS_MODULE; ++ ++ pfile = create_proc_read_entry("isoc_data", ++ 0444, pdir, ++ isoc_read_data, ++ NULL); ++ if (pfile == NULL) { ++ retval = -ENOMEM; ++ printk("Error creating file\n"); ++ goto no_file; ++ } ++ pfile->owner = THIS_MODULE; ++ ++ return usb_gadget_register_driver (&zero_driver); ++ ++ no_file: ++ remove_proc_entry("isoc_data", NULL); ++ done: ++ return retval; ++} ++module_init (init); ++ ++static void __exit cleanup (void) ++{ ++ ++ usb_gadget_unregister_driver (&zero_driver); ++ ++ remove_proc_entry("isoc_data", pdir); ++ remove_proc_entry("isoc_test", NULL); ++} ++module_exit (cleanup); +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_cfi_common.h +@@ -0,0 +1,142 @@ ++/* ========================================================================== ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_CFI_COMMON_H__) ++#define __DWC_CFI_COMMON_H__ ++ ++//#include <linux/types.h> ++ ++/** ++ * @file ++ * ++ * This file contains the CFI specific common constants, interfaces ++ * (functions and macros) and structures for Linux. No PCD specific ++ * data structure or definition is to be included in this file. ++ * ++ */ ++ ++/** This is a request for all Core Features */ ++#define VEN_CORE_GET_FEATURES 0xB1 ++ ++/** This is a request to get the value of a specific Core Feature */ ++#define VEN_CORE_GET_FEATURE 0xB2 ++ ++/** This command allows the host to set the value of a specific Core Feature */ ++#define VEN_CORE_SET_FEATURE 0xB3 ++ ++/** This command allows the host to set the default values of ++ * either all or any specific Core Feature ++ */ ++#define VEN_CORE_RESET_FEATURES 0xB4 ++ ++/** This command forces the PCD to write the deferred values of a Core Features */ ++#define VEN_CORE_ACTIVATE_FEATURES 0xB5 ++ ++/** This request reads a DWORD value from a register at the specified offset */ ++#define VEN_CORE_READ_REGISTER 0xB6 ++ ++/** This request writes a DWORD value into a register at the specified offset */ ++#define VEN_CORE_WRITE_REGISTER 0xB7 ++ ++/** This structure is the header of the Core Features dataset returned to ++ * the Host ++ */ ++struct cfi_all_features_header { ++/** The features header structure length is */ ++#define CFI_ALL_FEATURES_HDR_LEN 8 ++ /** ++ * The total length of the features dataset returned to the Host ++ */ ++ uint16_t wTotalLen; ++ ++ /** ++ * CFI version number inBinary-Coded Decimal (i.e., 1.00 is 100H). ++ * This field identifies the version of the CFI Specification with which ++ * the device is compliant. ++ */ ++ uint16_t wVersion; ++ ++ /** The ID of the Core */ ++ uint16_t wCoreID; ++#define CFI_CORE_ID_UDC 1 ++#define CFI_CORE_ID_OTG 2 ++#define CFI_CORE_ID_WUDEV 3 ++ ++ /** Number of features returned by VEN_CORE_GET_FEATURES request */ ++ uint16_t wNumFeatures; ++} UPACKED; ++ ++typedef struct cfi_all_features_header cfi_all_features_header_t; ++ ++/** This structure is a header of the Core Feature descriptor dataset returned to ++ * the Host after the VEN_CORE_GET_FEATURES request ++ */ ++struct cfi_feature_desc_header { ++#define CFI_FEATURE_DESC_HDR_LEN 8 ++ ++ /** The feature ID */ ++ uint16_t wFeatureID; ++ ++ /** Length of this feature descriptor in bytes - including the ++ * length of the feature name string ++ */ ++ uint16_t wLength; ++ ++ /** The data length of this feature in bytes */ ++ uint16_t wDataLength; ++ ++ /** ++ * Attributes of this features ++ * D0: Access rights ++ * 0 - Read/Write ++ * 1 - Read only ++ */ ++ uint8_t bmAttributes; ++#define CFI_FEATURE_ATTR_RO 1 ++#define CFI_FEATURE_ATTR_RW 0 ++ ++ /** Length of the feature name in bytes */ ++ uint8_t bNameLen; ++ ++ /** The feature name buffer */ ++ //uint8_t *name; ++} UPACKED; ++ ++typedef struct cfi_feature_desc_header cfi_feature_desc_header_t; ++ ++/** ++ * This structure describes a NULL terminated string referenced by its id field. ++ * It is very similar to usb_string structure but has the id field type set to 16-bit. ++ */ ++struct cfi_string { ++ uint16_t id; ++ const uint8_t *s; ++}; ++typedef struct cfi_string cfi_string_t; ++ ++#endif +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_attr.c +@@ -0,0 +1,1316 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.c $ ++ * $Revision: #35 $ ++ * $Date: 2009/04/03 $ ++ * $Change: 1225160 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The diagnostic interface will provide access to the controller for ++ * bringing up the hardware and testing. The Linux driver attributes ++ * feature will be used to provide the Linux Diagnostic ++ * Interface. These attributes are accessed through sysfs. ++ */ ++ ++/** @page "Linux Module Attributes" ++ * ++ * The Linux module attributes feature is used to provide the Linux ++ * Diagnostic Interface. These attributes are accessed through sysfs. ++ * The diagnostic interface will provide access to the controller for ++ * bringing up the hardware and testing. ++ ++ The following table shows the attributes. ++ <table> ++ <tr> ++ <td><b> Name</b></td> ++ <td><b> Description</b></td> ++ <td><b> Access</b></td> ++ </tr> ++ ++ <tr> ++ <td> mode </td> ++ <td> Returns the current mode: 0 for device mode, 1 for host mode</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> hnpcapable </td> ++ <td> Gets or sets the "HNP-capable" bit in the Core USB Configuraton Register. ++ Read returns the current value.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> srpcapable </td> ++ <td> Gets or sets the "SRP-capable" bit in the Core USB Configuraton Register. ++ Read returns the current value.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> hsic_connect </td> ++ <td> Gets or sets the "HSIC-Connect" bit in the GLPMCFG Register. ++ Read returns the current value.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> inv_sel_hsic </td> ++ <td> Gets or sets the "Invert Select HSIC" bit in the GLPMFG Register. ++ Read returns the current value.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> hnp </td> ++ <td> Initiates the Host Negotiation Protocol. Read returns the status.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> srp </td> ++ <td> Initiates the Session Request Protocol. Read returns the status.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> buspower </td> ++ <td> Gets or sets the Power State of the bus (0 - Off or 1 - On)</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> bussuspend </td> ++ <td> Suspends the USB bus.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> busconnected </td> ++ <td> Gets the connection status of the bus</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> gotgctl </td> ++ <td> Gets or sets the Core Control Status Register.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> gusbcfg </td> ++ <td> Gets or sets the Core USB Configuration Register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> grxfsiz </td> ++ <td> Gets or sets the Receive FIFO Size Register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> gnptxfsiz </td> ++ <td> Gets or sets the non-periodic Transmit Size Register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> gpvndctl </td> ++ <td> Gets or sets the PHY Vendor Control Register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> ggpio </td> ++ <td> Gets the value in the lower 16-bits of the General Purpose IO Register ++ or sets the upper 16 bits.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> guid </td> ++ <td> Gets or sets the value of the User ID Register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> gsnpsid </td> ++ <td> Gets the value of the Synopsys ID Regester</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> devspeed </td> ++ <td> Gets or sets the device speed setting in the DCFG register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> enumspeed </td> ++ <td> Gets the device enumeration Speed.</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> hptxfsiz </td> ++ <td> Gets the value of the Host Periodic Transmit FIFO</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> hprt0 </td> ++ <td> Gets or sets the value in the Host Port Control and Status Register</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> regoffset </td> ++ <td> Sets the register offset for the next Register Access</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> regvalue </td> ++ <td> Gets or sets the value of the register at the offset in the regoffset attribute.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> remote_wakeup </td> ++ <td> On read, shows the status of Remote Wakeup. On write, initiates a remote ++ wakeup of the host. When bit 0 is 1 and Remote Wakeup is enabled, the Remote ++ Wakeup signalling bit in the Device Control Register is set for 1 ++ milli-second.</td> ++ <td> Read/Write</td> ++ </tr> ++ ++ <tr> ++ <td> regdump </td> ++ <td> Dumps the contents of core registers.</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> spramdump </td> ++ <td> Dumps the contents of core registers.</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> hcddump </td> ++ <td> Dumps the current HCD state.</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> hcd_frrem </td> ++ <td> Shows the average value of the Frame Remaining ++ field in the Host Frame Number/Frame Remaining register when an SOF interrupt ++ occurs. This can be used to determine the average interrupt latency. Also ++ shows the average Frame Remaining value for start_transfer and the "a" and ++ "b" sample points. The "a" and "b" sample points may be used during debugging ++ bto determine how long it takes to execute a section of the HCD code.</td> ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> rd_reg_test </td> ++ <td> Displays the time required to read the GNPTXFSIZ register many times ++ (the output shows the number of times the register is read). ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> wr_reg_test </td> ++ <td> Displays the time required to write the GNPTXFSIZ register many times ++ (the output shows the number of times the register is written). ++ <td> Read</td> ++ </tr> ++ ++ <tr> ++ <td> lpm_response </td> ++ <td> Gets or sets lpm_response mode. Applicable only in device mode. ++ <td> Write</td> ++ </tr> ++ ++ <tr> ++ <td> sleep_local_dev </td> ++ <td> Generetates sleep signaling. Applicable only in host mode. ++ <td> Write</td> ++ </tr> ++ ++ <tr> ++ <td> sleep_status </td> ++ <td> Shows sleep status of device. ++ <td> Read</td> ++ </tr> ++ ++ </table> ++ ++ Example usage: ++ To get the current mode: ++ cat /sys/devices/lm0/mode ++ ++ To power down the USB: ++ echo 0 > /sys/devices/lm0/buspower ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/errno.h> ++#include <linux/types.h> ++#include <linux/stat.h> /* permission constants */ ++#include <linux/version.h> ++#include <linux/param.h> ++#include <linux/delay.h> ++#include <linux/jiffies.h> ++ ++ ++#ifdef LM_INTERFACE ++#include <asm/sizes.h> ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++#include <asm/arch/lm.h> ++#else ++/* in 2.6.31, at least, we seem to have lost the generic LM infrastructure - ++ here we use definitions stolen from arm-integrator headers ++*/ ++#include <mach/lm.h> ++#endif ++#elif defined(PLATFORM_INTERFACE) ++#include <linux/platform_device.h> ++#endif ++ ++#include <asm/io.h> ++ ++#include "dwc_os.h" ++#include "dwc_otg_driver.h" ++#include "dwc_otg_attr.h" ++#include "dwc_otg_core_if.h" ++#include "dwc_otg_pcd_if.h" ++#include "dwc_otg_hcd_if.h" ++ ++/* ++ * MACROs for defining sysfs attribute ++ */ ++#ifdef LM_INTERFACE ++ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \ ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \ ++ uint32_t val; \ ++ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \ ++ return sprintf (buf, "%s = 0x%x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \ ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \ ++ uint32_t set = simple_strtoul(buf, NULL, 16); \ ++ dwc_otg_set_##_otg_attr_name_(otg_dev->core_if, set);\ ++ return count; \ ++} ++ ++#elif defined(PCI_INTERFACE) ++ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \ ++ uint32_t val; \ ++ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \ ++ return sprintf (buf, "%s = 0x%x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \ ++ uint32_t set = simple_strtoul(buf, NULL, 16); \ ++ dwc_otg_set_##_otg_attr_name_(otg_dev->core_if, set);\ ++ return count; \ ++} ++ ++#elif defined(PLATFORM_INTERFACE) ++ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct platform_device *platform_dev = \ ++ container_of(_dev, struct platform_device, dev); \ ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); \ ++ uint32_t val; \ ++ DWC_PRINTF("%s(%p) -> platform_dev %p, otg_dev %p\n", \ ++ __func__, _dev, platform_dev, otg_dev); \ ++ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \ ++ return sprintf (buf, "%s = 0x%x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ struct platform_device *platform_dev = container_of(_dev, struct platform_device, dev); \ ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); \ ++ uint32_t set = simple_strtoul(buf, NULL, 16); \ ++ dwc_otg_set_##_otg_attr_name_(otg_dev->core_if, set);\ ++ return count; \ ++} ++#endif ++ ++/* ++ * MACROs for defining sysfs attribute for 32-bit registers ++ */ ++#ifdef LM_INTERFACE ++#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \ ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \ ++ uint32_t val; \ ++ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \ ++ return sprintf (buf, "%s = 0x%08x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); \ ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); \ ++ uint32_t val = simple_strtoul(buf, NULL, 16); \ ++ dwc_otg_set_##_otg_attr_name_ (otg_dev->core_if, val); \ ++ return count; \ ++} ++#elif defined(PCI_INTERFACE) ++#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \ ++ uint32_t val; \ ++ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \ ++ return sprintf (buf, "%s = 0x%08x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); \ ++ uint32_t val = simple_strtoul(buf, NULL, 16); \ ++ dwc_otg_set_##_otg_attr_name_ (otg_dev->core_if, val); \ ++ return count; \ ++} ++ ++#elif defined(PLATFORM_INTERFACE) ++#include "dwc_otg_dbg.h" ++#define DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_show (struct device *_dev, struct device_attribute *attr, char *buf) \ ++{ \ ++ struct platform_device *platform_dev = container_of(_dev, struct platform_device, dev); \ ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); \ ++ uint32_t val; \ ++ DWC_PRINTF("%s(%p) -> platform_dev %p, otg_dev %p\n", \ ++ __func__, _dev, platform_dev, otg_dev); \ ++ val = dwc_otg_get_##_otg_attr_name_ (otg_dev->core_if); \ ++ return sprintf (buf, "%s = 0x%08x\n", _string_, val); \ ++} ++#define DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \ ++static ssize_t _otg_attr_name_##_store (struct device *_dev, struct device_attribute *attr, \ ++ const char *buf, size_t count) \ ++{ \ ++ struct platform_device *platform_dev = container_of(_dev, struct platform_device, dev); \ ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); \ ++ uint32_t val = simple_strtoul(buf, NULL, 16); \ ++ dwc_otg_set_##_otg_attr_name_ (otg_dev->core_if, val); \ ++ return count; \ ++} ++ ++#endif ++ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_RW(_otg_attr_name_,_string_) \ ++DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \ ++DWC_OTG_DEVICE_ATTR_BITFIELD_STORE(_otg_attr_name_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store); ++ ++#define DWC_OTG_DEVICE_ATTR_BITFIELD_RO(_otg_attr_name_,_string_) \ ++DWC_OTG_DEVICE_ATTR_BITFIELD_SHOW(_otg_attr_name_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL); ++ ++#define DWC_OTG_DEVICE_ATTR_REG32_RW(_otg_attr_name_,_addr_,_string_) \ ++DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \ ++DWC_OTG_DEVICE_ATTR_REG_STORE(_otg_attr_name_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0644,_otg_attr_name_##_show,_otg_attr_name_##_store); ++ ++#define DWC_OTG_DEVICE_ATTR_REG32_RO(_otg_attr_name_,_addr_,_string_) \ ++DWC_OTG_DEVICE_ATTR_REG_SHOW(_otg_attr_name_,_string_) \ ++DEVICE_ATTR(_otg_attr_name_,0444,_otg_attr_name_##_show,NULL); ++ ++/** @name Functions for Show/Store of Attributes */ ++/**@{*/ ++ ++/** ++ * Show the register offset of the Register Access. ++ */ ++static ssize_t regoffset_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = container_of(_dev, ++ struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ return snprintf(buf, sizeof("0xFFFFFFFF\n") + 1, "0x%08x\n", ++ otg_dev->reg_offset); ++} ++ ++/** ++ * Set the register offset for the next Register Access Read/Write ++ */ ++static ssize_t regoffset_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = container_of(_dev, ++ struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ uint32_t offset = simple_strtoul(buf, NULL, 16); ++ if (offset < SZ_256K) { ++ otg_dev->reg_offset = offset; ++ } else { ++ dev_err(_dev, "invalid offset\n"); ++ } ++ ++ return count; ++} ++ ++DEVICE_ATTR(regoffset, S_IRUGO | S_IWUSR, regoffset_show, regoffset_store); ++ ++/** ++ * Show the value of the register at the offset in the reg_offset ++ * attribute. ++ */ ++static ssize_t regvalue_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ uint32_t val; ++ volatile uint32_t *addr; ++ ++ if (otg_dev->reg_offset != 0xFFFFFFFF && 0 != otg_dev->base) { ++ /* Calculate the address */ ++ addr = (uint32_t *) (otg_dev->reg_offset + ++ (uint8_t *) otg_dev->base); ++ val = dwc_read_reg32(addr); ++ return snprintf(buf, ++ sizeof("Reg@0xFFFFFFFF = 0xFFFFFFFF\n") + 1, ++ "Reg@0x%06x = 0x%08x\n", otg_dev->reg_offset, ++ val); ++ } else { ++ dev_err(_dev, "Invalid offset (0x%0x)\n", otg_dev->reg_offset); ++ return sprintf(buf, "invalid offset\n"); ++ } ++} ++ ++/** ++ * Store the value in the register at the offset in the reg_offset ++ * attribute. ++ * ++ */ ++static ssize_t regvalue_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ volatile uint32_t *addr; ++ uint32_t val = simple_strtoul(buf, NULL, 16); ++ //dev_dbg(_dev, "Offset=0x%08x Val=0x%08x\n", otg_dev->reg_offset, val); ++ if (otg_dev->reg_offset != 0xFFFFFFFF && 0 != otg_dev->base) { ++ /* Calculate the address */ ++ addr = (uint32_t *) (otg_dev->reg_offset + ++ (uint8_t *) otg_dev->base); ++ dwc_write_reg32(addr, val); ++ } else { ++ dev_err(_dev, "Invalid Register Offset (0x%08x)\n", ++ otg_dev->reg_offset); ++ } ++ return count; ++} ++ ++DEVICE_ATTR(regvalue, S_IRUGO | S_IWUSR, regvalue_show, regvalue_store); ++ ++/* ++ * Attributes ++ */ ++DWC_OTG_DEVICE_ATTR_BITFIELD_RO(mode, "Mode"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(hnpcapable, "HNPCapable"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(srpcapable, "Mode"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(hsic_connect, "HSIC Connect"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(inv_sel_hsic, "Invert Select HSIC"); ++ ++//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(buspower,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode"); ++//DWC_OTG_DEVICE_ATTR_BITFIELD_RW(bussuspend,&(otg_dev->core_if->core_global_regs->gotgctl),(1<<8),8,"Mode"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RO(busconnected, "Bus Connected"); ++ ++DWC_OTG_DEVICE_ATTR_REG32_RW(gotgctl, 0, "GOTGCTL"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(gusbcfg, ++ &(otg_dev->core_if->core_global_regs->gusbcfg), ++ "GUSBCFG"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(grxfsiz, ++ &(otg_dev->core_if->core_global_regs->grxfsiz), ++ "GRXFSIZ"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(gnptxfsiz, ++ &(otg_dev->core_if->core_global_regs->gnptxfsiz), ++ "GNPTXFSIZ"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(gpvndctl, ++ &(otg_dev->core_if->core_global_regs->gpvndctl), ++ "GPVNDCTL"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(ggpio, ++ &(otg_dev->core_if->core_global_regs->ggpio), ++ "GGPIO"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(guid, &(otg_dev->core_if->core_global_regs->guid), ++ "GUID"); ++DWC_OTG_DEVICE_ATTR_REG32_RO(gsnpsid, ++ &(otg_dev->core_if->core_global_regs->gsnpsid), ++ "GSNPSID"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RW(devspeed, "Device Speed"); ++DWC_OTG_DEVICE_ATTR_BITFIELD_RO(enumspeed, "Device Enumeration Speed"); ++ ++DWC_OTG_DEVICE_ATTR_REG32_RO(hptxfsiz, ++ &(otg_dev->core_if->core_global_regs->hptxfsiz), ++ "HPTXFSIZ"); ++DWC_OTG_DEVICE_ATTR_REG32_RW(hprt0, otg_dev->core_if->host_if->hprt0, "HPRT0"); ++ ++/** ++ * @todo Add code to initiate the HNP. ++ */ ++/** ++ * Show the HNP status bit ++ */ ++static ssize_t hnp_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ return sprintf(buf, "HstNegScs = 0x%x\n", ++ dwc_otg_get_hnpstatus(otg_dev->core_if)); ++} ++ ++/** ++ * Set the HNP Request bit ++ */ ++static ssize_t hnp_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ uint32_t in = simple_strtoul(buf, NULL, 16); ++ dwc_otg_set_hnpreq(otg_dev->core_if, in); ++ return count; ++} ++ ++DEVICE_ATTR(hnp, 0644, hnp_show, hnp_store); ++ ++/** ++ * @todo Add code to initiate the SRP. ++ */ ++/** ++ * Show the SRP status bit ++ */ ++static ssize_t srp_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifndef DWC_HOST_ONLY ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ return sprintf(buf, "SesReqScs = 0x%x\n", ++ dwc_otg_get_srpstatus(otg_dev->core_if)); ++#else ++ return sprintf(buf, "Host Only Mode!\n"); ++#endif ++} ++ ++/** ++ * Set the SRP Request bit ++ */ ++static ssize_t srp_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++#ifndef DWC_HOST_ONLY ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ dwc_otg_pcd_initiate_srp(otg_dev->pcd); ++#endif ++ return count; ++} ++ ++DEVICE_ATTR(srp, 0644, srp_show, srp_store); ++ ++/** ++ * @todo Need to do more for power on/off? ++ */ ++/** ++ * Show the Bus Power status ++ */ ++static ssize_t buspower_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ return sprintf(buf, "Bus Power = 0x%x\n", ++ dwc_otg_get_prtpower(otg_dev->core_if)); ++} ++ ++/** ++ * Set the Bus Power status ++ */ ++static ssize_t buspower_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ uint32_t on = simple_strtoul(buf, NULL, 16); ++ dwc_otg_set_prtpower(otg_dev->core_if, on); ++ return count; ++} ++ ++DEVICE_ATTR(buspower, 0644, buspower_show, buspower_store); ++ ++/** ++ * @todo Need to do more for suspend? ++ */ ++/** ++ * Show the Bus Suspend status ++ */ ++static ssize_t bussuspend_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ return sprintf(buf, "Bus Suspend = 0x%x\n", ++ dwc_otg_get_prtsuspend(otg_dev->core_if)); ++} ++ ++/** ++ * Set the Bus Suspend status ++ */ ++static ssize_t bussuspend_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ uint32_t in = simple_strtoul(buf, NULL, 16); ++ dwc_otg_set_prtsuspend(otg_dev->core_if, in); ++ return count; ++} ++ ++DEVICE_ATTR(bussuspend, 0644, bussuspend_show, bussuspend_store); ++ ++/** ++ * Show the status of Remote Wakeup. ++ */ ++static ssize_t remote_wakeup_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifndef DWC_HOST_ONLY ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ return sprintf(buf, ++ "Remote Wakeup Sig = %d Enabled = %d LPM Remote Wakeup = %d\n", ++ dwc_otg_get_remotewakesig(otg_dev->core_if), ++ dwc_otg_pcd_get_rmwkup_enable(otg_dev->pcd), ++ dwc_otg_get_lpm_remotewakeenabled(otg_dev->core_if)); ++#else ++ return sprintf(buf, "Host Only Mode!\n"); ++#endif /* DWC_HOST_ONLY */ ++} ++ ++/** ++ * Initiate a remote wakeup of the host. The Device control register ++ * Remote Wakeup Signal bit is written if the PCD Remote wakeup enable ++ * flag is set. ++ * ++ */ ++static ssize_t remote_wakeup_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++#ifndef DWC_HOST_ONLY ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ uint32_t val = simple_strtoul(buf, NULL, 16); ++ ++ if (val & 1) { ++ dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 1); ++ } else { ++ dwc_otg_pcd_remote_wakeup(otg_dev->pcd, 0); ++ } ++#endif /* DWC_HOST_ONLY */ ++ return count; ++} ++ ++DEVICE_ATTR(remote_wakeup, S_IRUGO | S_IWUSR, remote_wakeup_show, ++ remote_wakeup_store); ++ ++/** ++ * Dump global registers and either host or device registers (depending on the ++ * current mode of the core). ++ */ ++static ssize_t regdump_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ dwc_otg_dump_global_registers(otg_dev->core_if); ++ if (dwc_otg_is_host_mode(otg_dev->core_if)) { ++ dwc_otg_dump_host_registers(otg_dev->core_if); ++ } else { ++ dwc_otg_dump_dev_registers(otg_dev->core_if); ++ ++ } ++ return sprintf(buf, "Register Dump\n"); ++} ++ ++DEVICE_ATTR(regdump, S_IRUGO | S_IWUSR, regdump_show, 0); ++ ++/** ++ * Dump global registers and either host or device registers (depending on the ++ * current mode of the core). ++ */ ++static ssize_t spramdump_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ dwc_otg_dump_spram(otg_dev->core_if); ++ ++ return sprintf(buf, "SPRAM Dump\n"); ++} ++ ++DEVICE_ATTR(spramdump, S_IRUGO | S_IWUSR, spramdump_show, 0); ++ ++/** ++ * Dump the current hcd state. ++ */ ++static ssize_t hcddump_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifndef DWC_DEVICE_ONLY ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ dwc_otg_hcd_dump_state(otg_dev->hcd); ++#endif /* DWC_DEVICE_ONLY */ ++ return sprintf(buf, "HCD Dump\n"); ++} ++ ++DEVICE_ATTR(hcddump, S_IRUGO | S_IWUSR, hcddump_show, 0); ++ ++/** ++ * Dump the average frame remaining at SOF. This can be used to ++ * determine average interrupt latency. Frame remaining is also shown for ++ * start transfer and two additional sample points. ++ */ ++static ssize_t hcd_frrem_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifndef DWC_DEVICE_ONLY ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ dwc_otg_hcd_dump_frrem(otg_dev->hcd); ++#endif /* DWC_DEVICE_ONLY */ ++ return sprintf(buf, "HCD Dump Frame Remaining\n"); ++} ++ ++DEVICE_ATTR(hcd_frrem, S_IRUGO | S_IWUSR, hcd_frrem_show, 0); ++ ++/** ++ * Displays the time required to read the GNPTXFSIZ register many times (the ++ * output shows the number of times the register is read). ++ */ ++#define RW_REG_COUNT 10000000 ++#define MSEC_PER_JIFFIE 1000/HZ ++static ssize_t rd_reg_test_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ int i; ++ int time; ++ int start_jiffies; ++ ++ printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n", ++ HZ, MSEC_PER_JIFFIE, loops_per_jiffy); ++ start_jiffies = jiffies; ++ for (i = 0; i < RW_REG_COUNT; i++) { ++ dwc_otg_get_gnptxfsiz(otg_dev->core_if); ++ } ++ time = jiffies - start_jiffies; ++ return sprintf(buf, ++ "Time to read GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n", ++ RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); ++} ++ ++DEVICE_ATTR(rd_reg_test, S_IRUGO | S_IWUSR, rd_reg_test_show, 0); ++ ++/** ++ * Displays the time required to write the GNPTXFSIZ register many times (the ++ * output shows the number of times the register is written). ++ */ ++static ssize_t wr_reg_test_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ uint32_t reg_val; ++ int i; ++ int time; ++ int start_jiffies; ++ ++ printk("HZ %d, MSEC_PER_JIFFIE %d, loops_per_jiffy %lu\n", ++ HZ, MSEC_PER_JIFFIE, loops_per_jiffy); ++ reg_val = dwc_otg_get_gnptxfsiz(otg_dev->core_if); ++ start_jiffies = jiffies; ++ for (i = 0; i < RW_REG_COUNT; i++) { ++ dwc_otg_set_gnptxfsiz(otg_dev->core_if, reg_val); ++ } ++ time = jiffies - start_jiffies; ++ return sprintf(buf, ++ "Time to write GNPTXFSIZ reg %d times: %d msecs (%d jiffies)\n", ++ RW_REG_COUNT, time * MSEC_PER_JIFFIE, time); ++} ++ ++DEVICE_ATTR(wr_reg_test, S_IRUGO | S_IWUSR, wr_reg_test_show, 0); ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ ++/** ++* Show the lpm_response attribute. ++*/ ++static ssize_t lpmresp_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if)) ++ return sprintf(buf, "** LPM is DISABLED **\n"); ++ ++ if (!dwc_otg_is_device_mode(otg_dev->core_if)) { ++ return sprintf(buf, "** Current mode is not device mode\n"); ++ } ++ return sprintf(buf, "lpm_response = %d\n", ++ dwc_otg_get_lpmresponse(otg_dev->core_if)); ++} ++ ++/** ++* Store the lpm_response attribute. ++*/ ++static ssize_t lpmresp_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ uint32_t val = simple_strtoul(buf, NULL, 16); ++ ++ if (!dwc_otg_get_param_lpm_enable(otg_dev->core_if)) { ++ return 0; ++ } ++ ++ if (!dwc_otg_is_device_mode(otg_dev->core_if)) { ++ return 0; ++ } ++ ++ dwc_otg_set_lpmresponse(otg_dev->core_if, val); ++ return count; ++} ++ ++DEVICE_ATTR(lpm_response, S_IRUGO | S_IWUSR, lpmresp_show, lpmresp_store); ++ ++/** ++* Show the sleep_status attribute. ++*/ ++static ssize_t sleepstatus_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ ++ return sprintf(buf, "Sleep Status = %d\n", ++ dwc_otg_get_lpm_portsleepstatus(otg_dev->core_if)); ++} ++ ++/** ++ * Store the sleep_status attribure. ++ */ ++static ssize_t sleepstatus_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++#ifdef LM_INTERFACE ++ struct lm_device *lm_dev = container_of(_dev, struct lm_device, dev); ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(lm_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = dev_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platform_dev = ++ container_of(_dev, struct platform_device, dev); ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(platform_dev); ++#endif ++ ++ dwc_otg_core_if_t *core_if = otg_dev->core_if; ++ ++ if (dwc_otg_get_lpm_portsleepstatus(otg_dev->core_if)) { ++ if (dwc_otg_is_host_mode(core_if)) { ++ ++ DWC_PRINTF("Host initiated resume\n"); ++ dwc_otg_set_prtresume(otg_dev->core_if, 1); ++ } ++ } ++ ++ return count; ++} ++ ++DEVICE_ATTR(sleep_status, S_IRUGO | S_IWUSR, sleepstatus_show, ++ sleepstatus_store); ++ ++#endif /* CONFIG_USB_DWC_OTG_LPM_ENABLE */ ++ ++/**@}*/ ++ ++/** ++ * Create the device files ++ */ ++void dwc_otg_attr_create ( ++#ifdef LM_INTERFACE ++ struct lm_device *dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *dev ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *dev ++#endif ++ ) ++ ++{ ++ int error; ++ ++ error = device_create_file(&dev->dev, &dev_attr_regoffset); ++ error = device_create_file(&dev->dev, &dev_attr_regvalue); ++ error = device_create_file(&dev->dev, &dev_attr_mode); ++ error = device_create_file(&dev->dev, &dev_attr_hnpcapable); ++ error = device_create_file(&dev->dev, &dev_attr_srpcapable); ++ error = device_create_file(&dev->dev, &dev_attr_hsic_connect); ++ error = device_create_file(&dev->dev, &dev_attr_inv_sel_hsic); ++ error = device_create_file(&dev->dev, &dev_attr_hnp); ++ error = device_create_file(&dev->dev, &dev_attr_srp); ++ error = device_create_file(&dev->dev, &dev_attr_buspower); ++ error = device_create_file(&dev->dev, &dev_attr_bussuspend); ++ error = device_create_file(&dev->dev, &dev_attr_busconnected); ++ error = device_create_file(&dev->dev, &dev_attr_gotgctl); ++ error = device_create_file(&dev->dev, &dev_attr_gusbcfg); ++ error = device_create_file(&dev->dev, &dev_attr_grxfsiz); ++ error = device_create_file(&dev->dev, &dev_attr_gnptxfsiz); ++ error = device_create_file(&dev->dev, &dev_attr_gpvndctl); ++ error = device_create_file(&dev->dev, &dev_attr_ggpio); ++ error = device_create_file(&dev->dev, &dev_attr_guid); ++ error = device_create_file(&dev->dev, &dev_attr_gsnpsid); ++ error = device_create_file(&dev->dev, &dev_attr_devspeed); ++ error = device_create_file(&dev->dev, &dev_attr_enumspeed); ++ error = device_create_file(&dev->dev, &dev_attr_hptxfsiz); ++ error = device_create_file(&dev->dev, &dev_attr_hprt0); ++ error = device_create_file(&dev->dev, &dev_attr_remote_wakeup); ++ error = device_create_file(&dev->dev, &dev_attr_regdump); ++ error = device_create_file(&dev->dev, &dev_attr_spramdump); ++ error = device_create_file(&dev->dev, &dev_attr_hcddump); ++ error = device_create_file(&dev->dev, &dev_attr_hcd_frrem); ++ error = device_create_file(&dev->dev, &dev_attr_rd_reg_test); ++ error = device_create_file(&dev->dev, &dev_attr_wr_reg_test); ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ error = device_create_file(&dev->dev, &dev_attr_lpm_response); ++ error = device_create_file(&dev->dev, &dev_attr_sleep_status); ++#endif ++} ++ ++/** ++ * Remove the device files ++ */ ++void dwc_otg_attr_remove ( ++#ifdef LM_INTERFACE ++ struct lm_device *dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *dev ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *dev ++#endif ++ ) ++ ++{ ++ device_remove_file(&dev->dev, &dev_attr_regoffset); ++ device_remove_file(&dev->dev, &dev_attr_regvalue); ++ device_remove_file(&dev->dev, &dev_attr_mode); ++ device_remove_file(&dev->dev, &dev_attr_hnpcapable); ++ device_remove_file(&dev->dev, &dev_attr_srpcapable); ++ device_remove_file(&dev->dev, &dev_attr_hsic_connect); ++ device_remove_file(&dev->dev, &dev_attr_inv_sel_hsic); ++ device_remove_file(&dev->dev, &dev_attr_hnp); ++ device_remove_file(&dev->dev, &dev_attr_srp); ++ device_remove_file(&dev->dev, &dev_attr_buspower); ++ device_remove_file(&dev->dev, &dev_attr_bussuspend); ++ device_remove_file(&dev->dev, &dev_attr_busconnected); ++ device_remove_file(&dev->dev, &dev_attr_gotgctl); ++ device_remove_file(&dev->dev, &dev_attr_gusbcfg); ++ device_remove_file(&dev->dev, &dev_attr_grxfsiz); ++ device_remove_file(&dev->dev, &dev_attr_gnptxfsiz); ++ device_remove_file(&dev->dev, &dev_attr_gpvndctl); ++ device_remove_file(&dev->dev, &dev_attr_ggpio); ++ device_remove_file(&dev->dev, &dev_attr_guid); ++ device_remove_file(&dev->dev, &dev_attr_gsnpsid); ++ device_remove_file(&dev->dev, &dev_attr_devspeed); ++ device_remove_file(&dev->dev, &dev_attr_enumspeed); ++ device_remove_file(&dev->dev, &dev_attr_hptxfsiz); ++ device_remove_file(&dev->dev, &dev_attr_hprt0); ++ device_remove_file(&dev->dev, &dev_attr_remote_wakeup); ++ device_remove_file(&dev->dev, &dev_attr_regdump); ++ device_remove_file(&dev->dev, &dev_attr_spramdump); ++ device_remove_file(&dev->dev, &dev_attr_hcddump); ++ device_remove_file(&dev->dev, &dev_attr_hcd_frrem); ++ device_remove_file(&dev->dev, &dev_attr_rd_reg_test); ++ device_remove_file(&dev->dev, &dev_attr_wr_reg_test); ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ device_remove_file(&dev->dev, &dev_attr_lpm_response); ++ device_remove_file(&dev->dev, &dev_attr_sleep_status); ++#endif ++} +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_attr.h +@@ -0,0 +1,88 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_attr.h $ ++ * $Revision: #11 $ ++ * $Date: 2009/04/03 $ ++ * $Change: 1225160 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_OTG_ATTR_H__) ++#define __DWC_OTG_ATTR_H__ ++ ++/** @file ++ * This file contains the interface to the Linux device attributes. ++ */ ++extern struct device_attribute dev_attr_regoffset; ++extern struct device_attribute dev_attr_regvalue; ++ ++extern struct device_attribute dev_attr_mode; ++extern struct device_attribute dev_attr_hnpcapable; ++extern struct device_attribute dev_attr_srpcapable; ++extern struct device_attribute dev_attr_hnp; ++extern struct device_attribute dev_attr_srp; ++extern struct device_attribute dev_attr_buspower; ++extern struct device_attribute dev_attr_bussuspend; ++extern struct device_attribute dev_attr_busconnected; ++extern struct device_attribute dev_attr_gotgctl; ++extern struct device_attribute dev_attr_gusbcfg; ++extern struct device_attribute dev_attr_grxfsiz; ++extern struct device_attribute dev_attr_gnptxfsiz; ++extern struct device_attribute dev_attr_gpvndctl; ++extern struct device_attribute dev_attr_ggpio; ++extern struct device_attribute dev_attr_guid; ++extern struct device_attribute dev_attr_gsnpsid; ++extern struct device_attribute dev_attr_devspeed; ++extern struct device_attribute dev_attr_enumspeed; ++extern struct device_attribute dev_attr_hptxfsiz; ++extern struct device_attribute dev_attr_hprt0; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++extern struct device_attribute dev_attr_lpm_response; ++extern struct device_attribute dev_attr_sleep_local_dev; ++extern struct device_attribute devi_attr_sleep_status; ++#endif ++ ++void dwc_otg_attr_create ( ++#ifdef LM_INTERFACE ++ struct lm_device *dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *dev ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *dev ++#endif ++ ); ++ ++void dwc_otg_attr_remove ( ++#ifdef LM_INTERFACE ++ struct lm_device *dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *dev ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *dev ++#endif ++ ); ++#endif +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_cfi.c +@@ -0,0 +1,1876 @@ ++/* ========================================================================== ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * This file contains the most of the CFI implementation for the OTG. ++ */ ++ ++#ifdef DWC_UTE_CFI ++ ++#include "dwc_otg_pcd.h" ++#include "dwc_otg_cfi.h" ++ ++/** This definition should actually migrate to the Portability Library */ ++#define DWC_CONSTANT_CPU_TO_LE16(x) (x) ++ ++extern dwc_otg_pcd_ep_t *get_ep_by_addr(dwc_otg_pcd_t * pcd, u16 wIndex); ++ ++static int cfi_core_features_buf(uint8_t * buf, uint16_t buflen); ++static int cfi_get_feature_value(uint8_t * buf, uint16_t buflen, ++ struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *ctrl_req); ++static int cfi_set_feature_value(struct dwc_otg_pcd *pcd); ++static int cfi_ep_get_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req); ++static int cfi_ep_get_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req); ++static int cfi_ep_get_align_val(uint8_t * buf, struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req); ++static int cfi_preproc_reset(struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req); ++static void cfi_free_ep_bs_dyn_data(cfi_ep_t * cfiep); ++ ++static uint16_t get_dfifo_size(dwc_otg_core_if_t * core_if); ++static int32_t get_rxfifo_size(dwc_otg_core_if_t * core_if, uint16_t wValue); ++static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue); ++ ++static uint8_t resize_fifos(dwc_otg_core_if_t * core_if); ++ ++/** This is the header of the all features descriptor */ ++static cfi_all_features_header_t all_props_desc_header = { ++ .wVersion = DWC_CONSTANT_CPU_TO_LE16(0x100), ++ .wCoreID = DWC_CONSTANT_CPU_TO_LE16(CFI_CORE_ID_OTG), ++ .wNumFeatures = DWC_CONSTANT_CPU_TO_LE16(9), ++}; ++ ++/** This is an array of statically allocated feature descriptors */ ++static cfi_feature_desc_header_t prop_descs[] = { ++ ++ /* FT_ID_DMA_MODE */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_MODE), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(1), ++ }, ++ ++ /* FT_ID_DMA_BUFFER_SETUP */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFFER_SETUP), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), ++ }, ++ ++ /* FT_ID_DMA_BUFF_ALIGN */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_BUFF_ALIGN), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), ++ }, ++ ++ /* FT_ID_DMA_CONCAT_SETUP */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CONCAT_SETUP), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ //.wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), ++ }, ++ ++ /* FT_ID_DMA_CIRCULAR */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DMA_CIRCULAR), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), ++ }, ++ ++ /* FT_ID_THRESHOLD_SETUP */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_THRESHOLD_SETUP), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(6), ++ }, ++ ++ /* FT_ID_DFIFO_DEPTH */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_DFIFO_DEPTH), ++ .bmAttributes = CFI_FEATURE_ATTR_RO, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), ++ }, ++ ++ /* FT_ID_TX_FIFO_DEPTH */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_TX_FIFO_DEPTH), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), ++ }, ++ ++ /* FT_ID_RX_FIFO_DEPTH */ ++ { ++ .wFeatureID = DWC_CONSTANT_CPU_TO_LE16(FT_ID_RX_FIFO_DEPTH), ++ .bmAttributes = CFI_FEATURE_ATTR_RW, ++ .wDataLength = DWC_CONSTANT_CPU_TO_LE16(2), ++ } ++}; ++ ++/** The table of feature names */ ++cfi_string_t prop_name_table[] = { ++ {FT_ID_DMA_MODE, "dma_mode"}, ++ {FT_ID_DMA_BUFFER_SETUP, "buffer_setup"}, ++ {FT_ID_DMA_BUFF_ALIGN, "buffer_align"}, ++ {FT_ID_DMA_CONCAT_SETUP, "concat_setup"}, ++ {FT_ID_DMA_CIRCULAR, "buffer_circular"}, ++ {FT_ID_THRESHOLD_SETUP, "threshold_setup"}, ++ {FT_ID_DFIFO_DEPTH, "dfifo_depth"}, ++ {FT_ID_TX_FIFO_DEPTH, "txfifo_depth"}, ++ {FT_ID_RX_FIFO_DEPTH, "rxfifo_depth"}, ++ {} ++}; ++ ++/************************************************************************/ ++ ++/** ++ * Returns the name of the feature by its ID ++ * or NULL if no featute ID matches. ++ * ++ */ ++const uint8_t *get_prop_name(uint16_t prop_id, int *len) ++{ ++ cfi_string_t *pstr; ++ *len = 0; ++ ++ for (pstr = prop_name_table; pstr && pstr->s; pstr++) { ++ if (pstr->id == prop_id) { ++ *len = DWC_STRLEN(pstr->s); ++ return pstr->s; ++ } ++ } ++ return NULL; ++} ++ ++/** ++ * This function handles all CFI specific control requests. ++ * ++ * Return a negative value to stall the DCE. ++ */ ++int cfi_setup(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl) ++{ ++ int retval = 0; ++ dwc_otg_pcd_ep_t *ep = NULL; ++ cfiobject_t *cfi = pcd->cfi; ++ struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd); ++ uint16_t wLen = DWC_LE16_TO_CPU(&ctrl->wLength); ++ uint16_t wValue = DWC_LE16_TO_CPU(&ctrl->wValue); ++ uint16_t wIndex = DWC_LE16_TO_CPU(&ctrl->wIndex); ++ uint32_t regaddr = 0; ++ uint32_t regval = 0; ++ ++ /* Save this Control Request in the CFI object. ++ * The data field will be assigned in the data stage completion CB function. ++ */ ++ cfi->ctrl_req = *ctrl; ++ cfi->ctrl_req.data = NULL; ++ ++ cfi->need_gadget_att = 0; ++ cfi->need_status_in_complete = 0; ++ ++ switch (ctrl->bRequest) { ++ case VEN_CORE_GET_FEATURES: ++ retval = cfi_core_features_buf(cfi->buf_in.buf, CFI_IN_BUF_LEN); ++ if (retval >= 0) { ++ //dump_msg(cfi->buf_in.buf, retval); ++ ep = &pcd->ep0; ++ ++ retval = min((uint16_t) retval, wLen); ++ /* Transfer this buffer to the host through the EP0-IN EP */ ++ ep->dwc_ep.dma_addr = cfi->buf_in.addr; ++ ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf; ++ ep->dwc_ep.xfer_buff = cfi->buf_in.buf; ++ ep->dwc_ep.xfer_len = retval; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; ++ ++ pcd->ep0_pending = 1; ++ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); ++ } ++ retval = 0; ++ break; ++ ++ case VEN_CORE_GET_FEATURE: ++ CFI_INFO("VEN_CORE_GET_FEATURE\n"); ++ retval = cfi_get_feature_value(cfi->buf_in.buf, CFI_IN_BUF_LEN, ++ pcd, ctrl); ++ if (retval >= 0) { ++ ep = &pcd->ep0; ++ ++ retval = min((uint16_t) retval, wLen); ++ /* Transfer this buffer to the host through the EP0-IN EP */ ++ ep->dwc_ep.dma_addr = cfi->buf_in.addr; ++ ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf; ++ ep->dwc_ep.xfer_buff = cfi->buf_in.buf; ++ ep->dwc_ep.xfer_len = retval; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; ++ ++ pcd->ep0_pending = 1; ++ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); ++ } ++ CFI_INFO("VEN_CORE_GET_FEATURE=%d\n", retval); ++ dump_msg(cfi->buf_in.buf, retval); ++ break; ++ ++ case VEN_CORE_SET_FEATURE: ++ CFI_INFO("VEN_CORE_SET_FEATURE\n"); ++ /* Set up an XFER to get the data stage of the control request, ++ * which is the new value of the feature to be modified. ++ */ ++ ep = &pcd->ep0; ++ ep->dwc_ep.is_in = 0; ++ ep->dwc_ep.dma_addr = cfi->buf_out.addr; ++ ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf; ++ ep->dwc_ep.xfer_buff = cfi->buf_out.buf; ++ ep->dwc_ep.xfer_len = wLen; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; ++ ++ pcd->ep0_pending = 1; ++ /* Read the control write's data stage */ ++ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); ++ retval = 0; ++ break; ++ ++ case VEN_CORE_RESET_FEATURES: ++ CFI_INFO("VEN_CORE_RESET_FEATURES\n"); ++ cfi->need_gadget_att = 1; ++ cfi->need_status_in_complete = 1; ++ retval = cfi_preproc_reset(pcd, ctrl); ++ CFI_INFO("VEN_CORE_RESET_FEATURES = (%d)\n", retval); ++ break; ++ ++ case VEN_CORE_ACTIVATE_FEATURES: ++ CFI_INFO("VEN_CORE_ACTIVATE_FEATURES\n"); ++ break; ++ ++ case VEN_CORE_READ_REGISTER: ++ CFI_INFO("VEN_CORE_READ_REGISTER\n"); ++ /* wValue optionally contains the HI WORD of the register offset and ++ * wIndex contains the LOW WORD of the register offset ++ */ ++ if (wValue == 0) { ++ /* @TODO - MAS - fix the access to the base field */ ++ regaddr = 0; ++ //regaddr = (uint32_t) pcd->otg_dev->base; ++ //GET_CORE_IF(pcd)->co ++ regaddr |= wIndex; ++ } else { ++ regaddr = (wValue << 16) | wIndex; ++ } ++ ++ /* Read a 32-bit value of the memory at the regaddr */ ++ regval = dwc_read_reg32((uint32_t *) regaddr); ++ ++ ep = &pcd->ep0; ++ dwc_memcpy(cfi->buf_in.buf, ®val, sizeof(uint32_t)); ++ ep->dwc_ep.is_in = 1; ++ ep->dwc_ep.dma_addr = cfi->buf_in.addr; ++ ep->dwc_ep.start_xfer_buff = cfi->buf_in.buf; ++ ep->dwc_ep.xfer_buff = cfi->buf_in.buf; ++ ep->dwc_ep.xfer_len = wLen; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; ++ ++ pcd->ep0_pending = 1; ++ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); ++ cfi->need_gadget_att = 0; ++ retval = 0; ++ break; ++ ++ case VEN_CORE_WRITE_REGISTER: ++ CFI_INFO("VEN_CORE_WRITE_REGISTER\n"); ++ /* Set up an XFER to get the data stage of the control request, ++ * which is the new value of the register to be modified. ++ */ ++ ep = &pcd->ep0; ++ ep->dwc_ep.is_in = 0; ++ ep->dwc_ep.dma_addr = cfi->buf_out.addr; ++ ep->dwc_ep.start_xfer_buff = cfi->buf_out.buf; ++ ep->dwc_ep.xfer_buff = cfi->buf_out.buf; ++ ep->dwc_ep.xfer_len = wLen; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; ++ ++ pcd->ep0_pending = 1; ++ /* Read the control write's data stage */ ++ dwc_otg_ep0_start_transfer(coreif, &ep->dwc_ep); ++ retval = 0; ++ break; ++ ++ default: ++ retval = -DWC_E_NOT_SUPPORTED; ++ break; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function prepares the core features descriptors and copies its ++ * raw representation into the buffer <buf>. ++ * ++ * The buffer structure is as follows: ++ * all_features_header (8 bytes) ++ * features_#1 (8 bytes + feature name string length) ++ * features_#2 (8 bytes + feature name string length) ++ * ..... ++ * features_#n - where n=the total count of feature descriptors ++ */ ++static int cfi_core_features_buf(uint8_t * buf, uint16_t buflen) ++{ ++ cfi_feature_desc_header_t *prop_hdr = prop_descs; ++ cfi_feature_desc_header_t *prop; ++ cfi_all_features_header_t *all_props_hdr = &all_props_desc_header; ++ cfi_all_features_header_t *tmp; ++ uint8_t *tmpbuf = buf; ++ const uint8_t *pname = NULL; ++ int i, j, namelen = 0, totlen; ++ ++ /* Prepare and copy the core features into the buffer */ ++ CFI_INFO("%s:\n", __func__); ++ ++ tmp = (cfi_all_features_header_t *) tmpbuf; ++ *tmp = *all_props_hdr; ++ tmpbuf += CFI_ALL_FEATURES_HDR_LEN; ++ ++ j = sizeof(prop_descs) / sizeof(cfi_all_features_header_t); ++ for (i = 0; i < j; i++, prop_hdr++) { ++ pname = get_prop_name(prop_hdr->wFeatureID, &namelen); ++ prop = (cfi_feature_desc_header_t *) tmpbuf; ++ *prop = *prop_hdr; ++ ++ prop->bNameLen = namelen; ++ prop->wLength = ++ DWC_CONSTANT_CPU_TO_LE16(CFI_FEATURE_DESC_HDR_LEN + ++ namelen); ++ ++ tmpbuf += CFI_FEATURE_DESC_HDR_LEN; ++ dwc_memcpy(tmpbuf, pname, namelen); ++ tmpbuf += namelen; ++ } ++ ++ totlen = tmpbuf - buf; ++ ++ if (totlen > 0) { ++ tmp = (cfi_all_features_header_t *) buf; ++ tmp->wTotalLen = DWC_CONSTANT_CPU_TO_LE16(totlen); ++ } ++ ++ return totlen; ++} ++ ++/** ++ * This function releases all the dynamic memory in the CFI object. ++ */ ++static void cfi_release(cfiobject_t * cfiobj) ++{ ++ cfi_ep_t *cfiep; ++ dwc_list_link_t *tmp; ++ ++ CFI_INFO("%s\n", __func__); ++ ++ if (cfiobj->buf_in.buf) { ++ dwc_dma_free(CFI_IN_BUF_LEN, cfiobj->buf_in.buf, ++ cfiobj->buf_in.addr); ++ cfiobj->buf_in.buf = NULL; ++ } ++ ++ if (cfiobj->buf_out.buf) { ++ dwc_dma_free(CFI_OUT_BUF_LEN, cfiobj->buf_out.buf, ++ cfiobj->buf_out.addr); ++ cfiobj->buf_out.buf = NULL; ++ } ++ ++ /* Free the Buffer Setup values for each EP */ ++ //list_for_each_entry(cfiep, &cfiobj->active_eps, lh) { ++ DWC_LIST_FOREACH(tmp, &cfiobj->active_eps) { ++ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ cfi_free_ep_bs_dyn_data(cfiep); ++ } ++} ++ ++/** ++ * This function frees the dynamically allocated EP buffer setup data. ++ */ ++static void cfi_free_ep_bs_dyn_data(cfi_ep_t * cfiep) ++{ ++ if (cfiep->bm_sg) { ++ dwc_free(cfiep->bm_sg); ++ cfiep->bm_sg = NULL; ++ } ++ ++ if (cfiep->bm_align) { ++ dwc_free(cfiep->bm_align); ++ cfiep->bm_align = NULL; ++ } ++ ++ if (cfiep->bm_concat) { ++ if (NULL != cfiep->bm_concat->wTxBytes) { ++ dwc_free(cfiep->bm_concat->wTxBytes); ++ cfiep->bm_concat->wTxBytes = NULL; ++ } ++ dwc_free(cfiep->bm_concat); ++ cfiep->bm_concat = NULL; ++ } ++} ++ ++/** ++ * This function initializes the default values of the features ++ * for a specific endpoint and should be called only once when ++ * the EP is enabled first time. ++ */ ++static int cfi_ep_init_defaults(struct dwc_otg_pcd *pcd, cfi_ep_t * cfiep) ++{ ++ int retval = 0; ++ ++ cfiep->bm_sg = dwc_alloc(sizeof(ddma_sg_buffer_setup_t)); ++ if (NULL == cfiep->bm_sg) { ++ CFI_INFO("Failed to allocate memory for SG feature value\n"); ++ return -DWC_E_NO_MEMORY; ++ } ++ dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t)); ++ ++ /* For the Concatenation feature's default value we do not allocate ++ * memory for the wTxBytes field - it will be done in the set_feature_value ++ * request handler. ++ */ ++ cfiep->bm_concat = dwc_alloc(sizeof(ddma_concat_buffer_setup_t)); ++ if (NULL == cfiep->bm_concat) { ++ CFI_INFO ++ ("Failed to allocate memory for CONCATENATION feature value\n"); ++ dwc_free(cfiep->bm_sg); ++ return -DWC_E_NO_MEMORY; ++ } ++ dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t)); ++ ++ cfiep->bm_align = dwc_alloc(sizeof(ddma_align_buffer_setup_t)); ++ if (NULL == cfiep->bm_align) { ++ CFI_INFO ++ ("Failed to allocate memory for Alignment feature value\n"); ++ dwc_free(cfiep->bm_sg); ++ dwc_free(cfiep->bm_concat); ++ return -DWC_E_NO_MEMORY; ++ } ++ dwc_memset(cfiep->bm_align, 0, sizeof(ddma_align_buffer_setup_t)); ++ ++ return retval; ++} ++ ++/** ++ * The callback function that notifies the CFI on the activation of ++ * an endpoint in the PCD. The following steps are done in this function: ++ * ++ * Create a dynamically allocated cfi_ep_t object (a CFI wrapper to the PCD's ++ * active endpoint) ++ * Create MAX_DMA_DESCS_PER_EP count DMA Descriptors for the EP ++ * Set the Buffer Mode to standard ++ * Initialize the default values for all EP modes (SG, Circular, Concat, Align) ++ * Add the cfi_ep_t object to the list of active endpoints in the CFI object ++ */ ++static int cfi_ep_enable(struct cfiobject *cfi, struct dwc_otg_pcd *pcd, ++ struct dwc_otg_pcd_ep *ep) ++{ ++ cfi_ep_t *cfiep; ++ int retval = -DWC_E_NOT_SUPPORTED; ++ ++ CFI_INFO("%s: epname=%s; epnum=0x%02x\n", __func__, ++ "EP_" /*ep->ep.name */ , ep->desc->bEndpointAddress); ++ /* MAS - Check whether this endpoint already is in the list */ ++ cfiep = get_cfi_ep_by_pcd_ep(cfi, ep); ++ ++ if (NULL == cfiep) { ++ /* Allocate a cfi_ep_t object */ ++ cfiep = dwc_alloc(sizeof(cfi_ep_t)); ++ if (NULL == cfiep) { ++ CFI_INFO ++ ("Unable to allocate memory for <cfiep> in function %s\n", ++ __func__); ++ return -DWC_E_NO_MEMORY; ++ } ++ dwc_memset(cfiep, 0, sizeof(cfi_ep_t)); ++ ++ /* Save the dwc_otg_pcd_ep pointer in the cfiep object */ ++ cfiep->ep = ep; ++ ++ /* Allocate the DMA Descriptors chain of MAX_DMA_DESCS_PER_EP count */ ++ ep->dwc_ep.descs = ++ dwc_dma_alloc(MAX_DMA_DESCS_PER_EP * ++ sizeof(dwc_otg_dma_desc_t), ++ &ep->dwc_ep.descs_dma_addr); ++ ++ if (NULL == ep->dwc_ep.descs) { ++ dwc_free(cfiep); ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ DWC_LIST_INIT(&cfiep->lh); ++ ++ /* Set the buffer mode to BM_STANDARD. It will be modified ++ * when building descriptors for a specific buffer mode */ ++ ep->dwc_ep.buff_mode = BM_STANDARD; ++ ++ /* Create and initialize the default values for this EP's Buffer modes */ ++ if ((retval = cfi_ep_init_defaults(pcd, cfiep)) < 0) ++ return retval; ++ ++ /* Add the cfi_ep_t object to the CFI object's list of active endpoints */ ++ DWC_LIST_INSERT_TAIL(&cfi->active_eps, &cfiep->lh); ++ retval = 0; ++ } else { /* The sought EP already is in the list */ ++ CFI_INFO("%s: The sought EP already is in the list\n", ++ __func__); ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function is called when the data stage of a 3-stage Control Write request ++ * is complete. ++ * ++ */ ++static int cfi_ctrl_write_complete(struct cfiobject *cfi, ++ struct dwc_otg_pcd *pcd) ++{ ++ uint32_t addr, reg_value; ++ uint16_t wIndex, wValue; ++ uint8_t bRequest; ++ uint8_t *buf = cfi->buf_out.buf; ++ //struct usb_ctrlrequest *ctrl_req = &cfi->ctrl_req_saved; ++ struct cfi_usb_ctrlrequest *ctrl_req = &cfi->ctrl_req; ++ int retval = -DWC_E_NOT_SUPPORTED; ++ ++ CFI_INFO("%s\n", __func__); ++ ++ bRequest = ctrl_req->bRequest; ++ wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex); ++ wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue); ++ ++ /* ++ * Save the pointer to the data stage in the ctrl_req's <data> field. ++ * The request should be already saved in the command stage by now. ++ */ ++ ctrl_req->data = cfi->buf_out.buf; ++ cfi->need_status_in_complete = 0; ++ cfi->need_gadget_att = 0; ++ ++ switch (bRequest) { ++ case VEN_CORE_WRITE_REGISTER: ++ /* The buffer contains raw data of the new value for the register */ ++ reg_value = *((uint32_t *) buf); ++ if (wValue == 0) { ++ addr = 0; ++ //addr = (uint32_t) pcd->otg_dev->base; ++ addr += wIndex; ++ } else { ++ addr = (wValue << 16) | wIndex; ++ } ++ ++ //writel(reg_value, addr); ++ ++ retval = 0; ++ cfi->need_status_in_complete = 1; ++ break; ++ ++ case VEN_CORE_SET_FEATURE: ++ /* The buffer contains raw data of the new value of the feature */ ++ retval = cfi_set_feature_value(pcd); ++ if (retval < 0) ++ return retval; ++ ++ cfi->need_status_in_complete = 1; ++ break; ++ ++ default: ++ break; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function builds the DMA descriptors for the SG buffer mode. ++ */ ++static void cfi_build_sg_descs(struct cfiobject *cfi, cfi_ep_t * cfiep, ++ dwc_otg_pcd_request_t * req) ++{ ++ struct dwc_otg_pcd_ep *ep = cfiep->ep; ++ ddma_sg_buffer_setup_t *sgval = cfiep->bm_sg; ++ struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs; ++ struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs; ++ dma_addr_t buff_addr = req->dma; ++ int i; ++ uint32_t txsize, off; ++ ++ txsize = sgval->wSize; ++ off = sgval->bOffset; ++ ++// CFI_INFO("%s: %s TXSIZE=0x%08x; OFFSET=0x%08x\n", ++// __func__, cfiep->ep->ep.name, txsize, off); ++ ++ for (i = 0; i < sgval->bCount; i++) { ++ desc->status.b.bs = BS_HOST_BUSY; ++ desc->buf = buff_addr; ++ desc->status.b.l = 0; ++ desc->status.b.ioc = 0; ++ desc->status.b.sp = 0; ++ desc->status.b.bytes = txsize; ++ desc->status.b.bs = BS_HOST_READY; ++ ++ /* Set the next address of the buffer */ ++ buff_addr += txsize + off; ++ desc_last = desc; ++ desc++; ++ } ++ ++ /* Set the last, ioc and sp bits on the Last DMA Descriptor */ ++ desc_last->status.b.l = 1; ++ desc_last->status.b.ioc = 1; ++ desc_last->status.b.sp = ep->dwc_ep.sent_zlp; ++ /* Save the last DMA descriptor pointer */ ++ cfiep->dma_desc_last = desc_last; ++ cfiep->desc_count = sgval->bCount; ++} ++ ++/** ++ * This function builds the DMA descriptors for the Concatenation buffer mode. ++ */ ++static void cfi_build_concat_descs(struct cfiobject *cfi, cfi_ep_t * cfiep, ++ dwc_otg_pcd_request_t * req) ++{ ++ struct dwc_otg_pcd_ep *ep = cfiep->ep; ++ ddma_concat_buffer_setup_t *concatval = cfiep->bm_concat; ++ struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs; ++ struct dwc_otg_dma_desc *desc_last = cfiep->ep->dwc_ep.descs; ++ dma_addr_t buff_addr = req->dma; ++ int i; ++ uint16_t *txsize; ++ ++ txsize = concatval->wTxBytes; ++ ++ for (i = 0; i < concatval->hdr.bDescCount; i++) { ++ desc->buf = buff_addr; ++ desc->status.b.bs = BS_HOST_BUSY; ++ desc->status.b.l = 0; ++ desc->status.b.ioc = 0; ++ desc->status.b.sp = 0; ++ desc->status.b.bytes = *txsize; ++ desc->status.b.bs = BS_HOST_READY; ++ ++ txsize++; ++ /* Set the next address of the buffer */ ++ buff_addr += UGETW(ep->desc->wMaxPacketSize); ++ desc_last = desc; ++ desc++; ++ } ++ ++ /* Set the last, ioc and sp bits on the Last DMA Descriptor */ ++ desc_last->status.b.l = 1; ++ desc_last->status.b.ioc = 1; ++ desc_last->status.b.sp = ep->dwc_ep.sent_zlp; ++ cfiep->dma_desc_last = desc_last; ++ cfiep->desc_count = concatval->hdr.bDescCount; ++} ++ ++/** ++ * This function builds the DMA descriptors for the Circular buffer mode ++ */ ++static void cfi_build_circ_descs(struct cfiobject *cfi, cfi_ep_t * cfiep, ++ dwc_otg_pcd_request_t * req) ++{ ++ /* @todo: MAS - add implementation when this feature needs to be tested */ ++} ++ ++/** ++ * This function builds the DMA descriptors for the Alignment buffer mode ++ */ ++static void cfi_build_align_descs(struct cfiobject *cfi, cfi_ep_t * cfiep, ++ dwc_otg_pcd_request_t * req) ++{ ++ struct dwc_otg_pcd_ep *ep = cfiep->ep; ++ ddma_align_buffer_setup_t *alignval = cfiep->bm_align; ++ struct dwc_otg_dma_desc *desc = cfiep->ep->dwc_ep.descs; ++ dma_addr_t buff_addr = req->dma; ++ ++ desc->status.b.bs = BS_HOST_BUSY; ++ desc->status.b.l = 1; ++ desc->status.b.ioc = 1; ++ desc->status.b.sp = ep->dwc_ep.sent_zlp; ++ desc->status.b.bytes = req->length; ++ /* Adjust the buffer alignment */ ++ desc->buf = (buff_addr + alignval->bAlign); ++ desc->status.b.bs = BS_HOST_READY; ++ cfiep->dma_desc_last = desc; ++ cfiep->desc_count = 1; ++} ++ ++/** ++ * This function builds the DMA descriptors chain for different modes of the ++ * buffer setup of an endpoint. ++ */ ++static void cfi_build_descriptors(struct cfiobject *cfi, ++ struct dwc_otg_pcd *pcd, ++ struct dwc_otg_pcd_ep *ep, ++ dwc_otg_pcd_request_t * req) ++{ ++ cfi_ep_t *cfiep; ++ ++ /* Get the cfiep by the dwc_otg_pcd_ep */ ++ cfiep = get_cfi_ep_by_pcd_ep(cfi, ep); ++ if (NULL == cfiep) { ++ CFI_INFO("%s: Unable to find a matching active endpoint\n", ++ __func__); ++ return; ++ } ++ ++ cfiep->xfer_len = req->length; ++ ++ /* Iterate through all the DMA descriptors */ ++ switch (cfiep->ep->dwc_ep.buff_mode) { ++ case BM_SG: ++ cfi_build_sg_descs(cfi, cfiep, req); ++ break; ++ ++ case BM_CONCAT: ++ cfi_build_concat_descs(cfi, cfiep, req); ++ break; ++ ++ case BM_CIRCULAR: ++ cfi_build_circ_descs(cfi, cfiep, req); ++ break; ++ ++ case BM_ALIGN: ++ cfi_build_align_descs(cfi, cfiep, req); ++ break; ++ ++ default: ++ break; ++ } ++} ++ ++/** ++ * Allocate DMA buffer for different Buffer modes. ++ */ ++static void *cfi_ep_alloc_buf(struct cfiobject *cfi, struct dwc_otg_pcd *pcd, ++ struct dwc_otg_pcd_ep *ep, dma_addr_t * dma, ++ unsigned size, gfp_t flags) ++{ ++ return dwc_dma_alloc(size, dma); ++} ++ ++/** ++ * This function initializes the CFI object. ++ */ ++int init_cfi(cfiobject_t * cfiobj) ++{ ++ CFI_INFO("%s\n", __func__); ++ ++ /* Allocate a buffer for IN XFERs */ ++ cfiobj->buf_in.buf = ++ dwc_dma_alloc(CFI_IN_BUF_LEN, &cfiobj->buf_in.addr); ++ if (NULL == cfiobj->buf_in.buf) { ++ CFI_INFO("Unable to allocate buffer for INs\n"); ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ /* Allocate a buffer for OUT XFERs */ ++ cfiobj->buf_out.buf = ++ dwc_dma_alloc(CFI_OUT_BUF_LEN, &cfiobj->buf_out.addr); ++ if (NULL == cfiobj->buf_out.buf) { ++ CFI_INFO("Unable to allocate buffer for OUT\n"); ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ /* Initialize the callback function pointers */ ++ cfiobj->ops.release = cfi_release; ++ cfiobj->ops.ep_enable = cfi_ep_enable; ++ cfiobj->ops.ctrl_write_complete = cfi_ctrl_write_complete; ++ cfiobj->ops.build_descriptors = cfi_build_descriptors; ++ cfiobj->ops.ep_alloc_buf = cfi_ep_alloc_buf; ++ ++ /* Initialize the list of active endpoints in the CFI object */ ++ DWC_LIST_INIT(&cfiobj->active_eps); ++ ++ return 0; ++} ++ ++/** ++ * This function reads the required feature's current value into the buffer ++ * ++ * @retval: Returns negative as error, or the data length of the feature ++ */ ++static int cfi_get_feature_value(uint8_t * buf, uint16_t buflen, ++ struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *ctrl_req) ++{ ++ int retval = -DWC_E_NOT_SUPPORTED; ++ struct dwc_otg_core_if *coreif = GET_CORE_IF(pcd); ++ uint16_t dfifo, rxfifo, txfifo; ++ ++ switch (ctrl_req->wIndex) { ++ /* Whether the DDMA is enabled or not */ ++ case FT_ID_DMA_MODE: ++ *buf = (coreif->dma_enable && coreif->dma_desc_enable) ? 1 : 0; ++ retval = 1; ++ break; ++ ++ case FT_ID_DMA_BUFFER_SETUP: ++ retval = cfi_ep_get_sg_val(buf, pcd, ctrl_req); ++ break; ++ ++ case FT_ID_DMA_BUFF_ALIGN: ++ retval = cfi_ep_get_align_val(buf, pcd, ctrl_req); ++ break; ++ ++ case FT_ID_DMA_CONCAT_SETUP: ++ retval = cfi_ep_get_concat_val(buf, pcd, ctrl_req); ++ break; ++ ++ case FT_ID_DMA_CIRCULAR: ++ CFI_INFO("GetFeature value (FT_ID_DMA_CIRCULAR)\n"); ++ break; ++ ++ case FT_ID_THRESHOLD_SETUP: ++ CFI_INFO("GetFeature value (FT_ID_THRESHOLD_SETUP)\n"); ++ break; ++ ++ case FT_ID_DFIFO_DEPTH: ++ dfifo = get_dfifo_size(coreif); ++ *((uint16_t *) buf) = dfifo; ++ retval = sizeof(uint16_t); ++ break; ++ ++ case FT_ID_TX_FIFO_DEPTH: ++ retval = get_txfifo_size(pcd, ctrl_req->wValue); ++ if (retval >= 0) { ++ txfifo = retval; ++ *((uint16_t *) buf) = txfifo; ++ retval = sizeof(uint16_t); ++ } ++ break; ++ ++ case FT_ID_RX_FIFO_DEPTH: ++ retval = get_rxfifo_size(coreif, ctrl_req->wValue); ++ if (retval >= 0) { ++ rxfifo = retval; ++ *((uint16_t *) buf) = rxfifo; ++ retval = sizeof(uint16_t); ++ } ++ break; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function resets the SG for the specified EP to its default value ++ */ ++static int cfi_reset_sg_val(cfi_ep_t * cfiep) ++{ ++ dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t)); ++ return 0; ++} ++ ++/** ++ * This function resets the Alignment for the specified EP to its default value ++ */ ++static int cfi_reset_align_val(cfi_ep_t * cfiep) ++{ ++ dwc_memset(cfiep->bm_sg, 0, sizeof(ddma_sg_buffer_setup_t)); ++ return 0; ++} ++ ++/** ++ * This function resets the Concatenation for the specified EP to its default value ++ * This function will also set the value of the wTxBytes field to NULL after ++ * freeing the memory previously allocated for this field. ++ */ ++static int cfi_reset_concat_val(cfi_ep_t * cfiep) ++{ ++ /* First we need to free the wTxBytes field */ ++ if (cfiep->bm_concat->wTxBytes) { ++ dwc_free(cfiep->bm_concat->wTxBytes); ++ cfiep->bm_concat->wTxBytes = NULL; ++ } ++ ++ dwc_memset(cfiep->bm_concat, 0, sizeof(ddma_concat_buffer_setup_t)); ++ return 0; ++} ++ ++/** ++ * This function resets all the buffer setups of the specified endpoint ++ */ ++static int cfi_ep_reset_all_setup_vals(cfi_ep_t * cfiep) ++{ ++ cfi_reset_sg_val(cfiep); ++ cfi_reset_align_val(cfiep); ++ cfi_reset_concat_val(cfiep); ++ return 0; ++} ++ ++static int cfi_handle_reset_fifo_val(struct dwc_otg_pcd *pcd, uint8_t ep_addr, ++ uint8_t rx_rst, uint8_t tx_rst) ++{ ++ int retval = -DWC_E_INVALID; ++ uint16_t tx_siz[15]; ++ uint16_t rx_siz = 0; ++ dwc_otg_pcd_ep_t *ep = NULL; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params; ++ ++ if (rx_rst) { ++ rx_siz = params->dev_rx_fifo_size; ++ params->dev_rx_fifo_size = GET_CORE_IF(pcd)->init_rxfsiz; ++ } ++ ++ if (tx_rst) { ++ if (ep_addr == 0) { ++ int i; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ tx_siz[i] = ++ core_if->core_params->dev_tx_fifo_size[i]; ++ core_if->core_params->dev_tx_fifo_size[i] = ++ core_if->init_txfsiz[i]; ++ } ++ } else { ++ ++ ep = get_ep_by_addr(pcd, ep_addr); ++ ++ if (NULL == ep) { ++ CFI_INFO ++ ("%s: Unable to get the endpoint addr=0x%02x\n", ++ __func__, ep_addr); ++ return -DWC_E_INVALID; ++ } ++ ++ tx_siz[0] = ++ params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - ++ 1]; ++ params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = ++ GET_CORE_IF(pcd)->init_txfsiz[ep->dwc_ep. ++ tx_fifo_num - 1]; ++ } ++ } ++ ++ if (resize_fifos(GET_CORE_IF(pcd))) { ++ retval = 0; ++ } else { ++ CFI_INFO ++ ("%s: Error resetting the feature Reset All(FIFO size)\n", ++ __func__); ++ if (rx_rst) { ++ params->dev_rx_fifo_size = rx_siz; ++ } ++ ++ if (tx_rst) { ++ if (ep_addr == 0) { ++ int i; ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; ++ i++) { ++ core_if->core_params-> ++ dev_tx_fifo_size[i] = tx_siz[i]; ++ } ++ } else { ++ params->dev_tx_fifo_size[ep->dwc_ep. ++ tx_fifo_num - 1] = ++ tx_siz[0]; ++ } ++ } ++ retval = -DWC_E_INVALID; ++ } ++ return retval; ++} ++ ++static int cfi_handle_reset_all(struct dwc_otg_pcd *pcd, uint8_t addr) ++{ ++ int retval = 0; ++ cfi_ep_t *cfiep; ++ cfiobject_t *cfi = pcd->cfi; ++ dwc_list_link_t *tmp; ++ ++ retval = cfi_handle_reset_fifo_val(pcd, addr, 1, 1); ++ if (retval < 0) { ++ return retval; ++ } ++ ++ /* If the EP address is known then reset the features for only that EP */ ++ if (addr) { ++ cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == cfiep) { ++ CFI_INFO("%s: Error getting the EP address 0x%02x\n", ++ __func__, addr); ++ return -DWC_E_INVALID; ++ } ++ retval = cfi_ep_reset_all_setup_vals(cfiep); ++ cfiep->ep->dwc_ep.buff_mode = BM_STANDARD; ++ } ++ /* Otherwise (wValue == 0), reset all features of all EP's */ ++ else { ++ /* Traverse all the active EP's and reset the feature(s) value(s) */ ++ //list_for_each_entry(cfiep, &cfi->active_eps, lh) { ++ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { ++ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ retval = cfi_ep_reset_all_setup_vals(cfiep); ++ cfiep->ep->dwc_ep.buff_mode = BM_STANDARD; ++ if (retval < 0) { ++ CFI_INFO ++ ("%s: Error resetting the feature Reset All\n", ++ __func__); ++ return retval; ++ } ++ } ++ } ++ return retval; ++} ++ ++static int cfi_handle_reset_dma_buff_setup(struct dwc_otg_pcd *pcd, ++ uint8_t addr) ++{ ++ int retval = 0; ++ cfi_ep_t *cfiep; ++ cfiobject_t *cfi = pcd->cfi; ++ dwc_list_link_t *tmp; ++ ++ /* If the EP address is known then reset the features for only that EP */ ++ if (addr) { ++ cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == cfiep) { ++ CFI_INFO("%s: Error getting the EP address 0x%02x\n", ++ __func__, addr); ++ return -DWC_E_INVALID; ++ } ++ retval = cfi_reset_sg_val(cfiep); ++ } ++ /* Otherwise (wValue == 0), reset all features of all EP's */ ++ else { ++ /* Traverse all the active EP's and reset the feature(s) value(s) */ ++ //list_for_each_entry(cfiep, &cfi->active_eps, lh) { ++ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { ++ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ retval = cfi_reset_sg_val(cfiep); ++ if (retval < 0) { ++ CFI_INFO ++ ("%s: Error resetting the feature Buffer Setup\n", ++ __func__); ++ return retval; ++ } ++ } ++ } ++ return retval; ++} ++ ++static int cfi_handle_reset_concat_val(struct dwc_otg_pcd *pcd, uint8_t addr) ++{ ++ int retval = 0; ++ cfi_ep_t *cfiep; ++ cfiobject_t *cfi = pcd->cfi; ++ dwc_list_link_t *tmp; ++ ++ /* If the EP address is known then reset the features for only that EP */ ++ if (addr) { ++ cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == cfiep) { ++ CFI_INFO("%s: Error getting the EP address 0x%02x\n", ++ __func__, addr); ++ return -DWC_E_INVALID; ++ } ++ retval = cfi_reset_concat_val(cfiep); ++ } ++ /* Otherwise (wValue == 0), reset all features of all EP's */ ++ else { ++ /* Traverse all the active EP's and reset the feature(s) value(s) */ ++ //list_for_each_entry(cfiep, &cfi->active_eps, lh) { ++ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { ++ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ retval = cfi_reset_concat_val(cfiep); ++ if (retval < 0) { ++ CFI_INFO ++ ("%s: Error resetting the feature Concatenation Value\n", ++ __func__); ++ return retval; ++ } ++ } ++ } ++ return retval; ++} ++ ++static int cfi_handle_reset_align_val(struct dwc_otg_pcd *pcd, uint8_t addr) ++{ ++ int retval = 0; ++ cfi_ep_t *cfiep; ++ cfiobject_t *cfi = pcd->cfi; ++ dwc_list_link_t *tmp; ++ ++ /* If the EP address is known then reset the features for only that EP */ ++ if (addr) { ++ cfiep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == cfiep) { ++ CFI_INFO("%s: Error getting the EP address 0x%02x\n", ++ __func__, addr); ++ return -DWC_E_INVALID; ++ } ++ retval = cfi_reset_align_val(cfiep); ++ } ++ /* Otherwise (wValue == 0), reset all features of all EP's */ ++ else { ++ /* Traverse all the active EP's and reset the feature(s) value(s) */ ++ //list_for_each_entry(cfiep, &cfi->active_eps, lh) { ++ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { ++ cfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ retval = cfi_reset_align_val(cfiep); ++ if (retval < 0) { ++ CFI_INFO ++ ("%s: Error resetting the feature Aliignment Value\n", ++ __func__); ++ return retval; ++ } ++ } ++ } ++ return retval; ++ ++} ++ ++static int cfi_preproc_reset(struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req) ++{ ++ int retval = 0; ++ ++ switch (req->wIndex) { ++ case 0: ++ /* Reset all features */ ++ retval = cfi_handle_reset_all(pcd, req->wValue & 0xff); ++ break; ++ ++ case FT_ID_DMA_BUFFER_SETUP: ++ /* Reset the SG buffer setup */ ++ retval = ++ cfi_handle_reset_dma_buff_setup(pcd, req->wValue & 0xff); ++ break; ++ ++ case FT_ID_DMA_CONCAT_SETUP: ++ /* Reset the Concatenation buffer setup */ ++ retval = cfi_handle_reset_concat_val(pcd, req->wValue & 0xff); ++ break; ++ ++ case FT_ID_DMA_BUFF_ALIGN: ++ /* Reset the Alignment buffer setup */ ++ retval = cfi_handle_reset_align_val(pcd, req->wValue & 0xff); ++ break; ++ ++ case FT_ID_TX_FIFO_DEPTH: ++ retval = ++ cfi_handle_reset_fifo_val(pcd, req->wValue & 0xff, 0, 1); ++ pcd->cfi->need_gadget_att = 0; ++ break; ++ ++ case FT_ID_RX_FIFO_DEPTH: ++ retval = cfi_handle_reset_fifo_val(pcd, 0, 1, 0); ++ pcd->cfi->need_gadget_att = 0; ++ break; ++ default: ++ break; ++ } ++ return retval; ++} ++ ++/** ++ * This function sets a new value for the SG buffer setup. ++ */ ++static int cfi_ep_set_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd) ++{ ++ uint8_t inaddr, outaddr; ++ cfi_ep_t *epin, *epout; ++ ddma_sg_buffer_setup_t *psgval; ++ uint32_t desccount, size; ++ ++ CFI_INFO("%s\n", __func__); ++ ++ psgval = (ddma_sg_buffer_setup_t *) buf; ++ desccount = (uint32_t) psgval->bCount; ++ size = (uint32_t) psgval->wSize; ++ ++ /* Check the DMA descriptor count */ ++ if ((desccount > MAX_DMA_DESCS_PER_EP) || (desccount == 0)) { ++ CFI_INFO ++ ("%s: The count of DMA Descriptors should be between 1 and %d\n", ++ __func__, MAX_DMA_DESCS_PER_EP); ++ return -DWC_E_INVALID; ++ } ++ ++ /* Check the DMA descriptor count */ ++ ++ if (size == 0) { ++ ++ CFI_INFO("%s: The transfer size should be at least 1 byte\n", ++ __func__); ++ ++ return -DWC_E_INVALID; ++ ++ } ++ ++ inaddr = psgval->bInEndpointAddress; ++ outaddr = psgval->bOutEndpointAddress; ++ ++ epin = get_cfi_ep_by_addr(pcd->cfi, inaddr); ++ epout = get_cfi_ep_by_addr(pcd->cfi, outaddr); ++ ++ if (NULL == epin || NULL == epout) { ++ CFI_INFO ++ ("%s: Unable to get the endpoints inaddr=0x%02x outaddr=0x%02x\n", ++ __func__, inaddr, outaddr); ++ return -DWC_E_INVALID; ++ } ++ ++ epin->ep->dwc_ep.buff_mode = BM_SG; ++ dwc_memcpy(epin->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t)); ++ ++ epout->ep->dwc_ep.buff_mode = BM_SG; ++ dwc_memcpy(epout->bm_sg, psgval, sizeof(ddma_sg_buffer_setup_t)); ++ ++ return 0; ++} ++ ++/** ++ * This function sets a new value for the buffer Alignment setup. ++ */ ++static int cfi_ep_set_alignment_val(uint8_t * buf, struct dwc_otg_pcd *pcd) ++{ ++ cfi_ep_t *ep; ++ uint8_t addr; ++ ddma_align_buffer_setup_t *palignval; ++ ++ palignval = (ddma_align_buffer_setup_t *) buf; ++ addr = palignval->bEndpointAddress; ++ ++ ep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", ++ __func__, addr); ++ return -DWC_E_INVALID; ++ } ++ ++ ep->ep->dwc_ep.buff_mode = BM_ALIGN; ++ dwc_memcpy(ep->bm_align, palignval, sizeof(ddma_align_buffer_setup_t)); ++ ++ return 0; ++} ++ ++/** ++ * This function sets a new value for the Concatenation buffer setup. ++ */ ++static int cfi_ep_set_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd) ++{ ++ uint8_t addr; ++ cfi_ep_t *ep; ++ struct _ddma_concat_buffer_setup_hdr *pConcatValHdr; ++ uint16_t *pVals; ++ uint32_t desccount; ++ int i; ++ uint16_t mps; ++ ++ pConcatValHdr = (struct _ddma_concat_buffer_setup_hdr *)buf; ++ desccount = (uint32_t) pConcatValHdr->bDescCount; ++ pVals = (uint16_t *) (buf + BS_CONCAT_VAL_HDR_LEN); ++ ++ /* Check the DMA descriptor count */ ++ if (desccount > MAX_DMA_DESCS_PER_EP) { ++ CFI_INFO("%s: Maximum DMA Descriptor count should be %d\n", ++ __func__, MAX_DMA_DESCS_PER_EP); ++ return -DWC_E_INVALID; ++ } ++ ++ addr = pConcatValHdr->bEndpointAddress; ++ ep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", ++ __func__, addr); ++ return -DWC_E_INVALID; ++ } ++ ++ mps = UGETW(ep->ep->desc->wMaxPacketSize); ++ ++#if 0 ++ for (i = 0; i < desccount; i++) { ++ CFI_INFO("%s: wTxSize[%d]=0x%04x\n", __func__, i, pVals[i]); ++ } ++ CFI_INFO("%s: epname=%s; mps=%d\n", __func__, ep->ep->ep.name, mps); ++#endif ++ ++ /* Check the wTxSizes to be less than or equal to the mps */ ++ for (i = 0; i < desccount; i++) { ++ if (pVals[i] > mps) { ++ CFI_INFO ++ ("%s: ERROR - the wTxSize[%d] should be <= MPS (wTxSize=%d)\n", ++ __func__, i, pVals[i]); ++ return -DWC_E_INVALID; ++ } ++ } ++ ++ ep->ep->dwc_ep.buff_mode = BM_CONCAT; ++ dwc_memcpy(ep->bm_concat, pConcatValHdr, BS_CONCAT_VAL_HDR_LEN); ++ ++ /* Free the previously allocated storage for the wTxBytes */ ++ if (ep->bm_concat->wTxBytes) { ++ dwc_free(ep->bm_concat->wTxBytes); ++ } ++ ++ /* Allocate a new storage for the wTxBytes field */ ++ ep->bm_concat->wTxBytes = ++ dwc_alloc(sizeof(uint16_t) * pConcatValHdr->bDescCount); ++ if (NULL == ep->bm_concat->wTxBytes) { ++ CFI_INFO("%s: Unable to allocate memory\n", __func__); ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ /* Copy the new values into the wTxBytes filed */ ++ dwc_memcpy(ep->bm_concat->wTxBytes, buf + BS_CONCAT_VAL_HDR_LEN, ++ sizeof(uint16_t) * pConcatValHdr->bDescCount); ++ ++ return 0; ++} ++ ++/** ++ * This function calculates the total of all FIFO sizes ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ * @return The total of data FIFO sizes. ++ * ++ */ ++static uint16_t get_dfifo_size(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_params_t *params = core_if->core_params; ++ uint16_t dfifo_total = 0; ++ int i; ++ ++ /* The shared RxFIFO size */ ++ dfifo_total = ++ params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size; ++ ++ /* Add up each TxFIFO size to the total */ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ dfifo_total += params->dev_tx_fifo_size[i]; ++ } ++ ++ return dfifo_total; ++} ++ ++/** ++ * This function returns Rx FIFO size ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ * @return The total of data FIFO sizes. ++ * ++ */ ++static int32_t get_rxfifo_size(dwc_otg_core_if_t * core_if, uint16_t wValue) ++{ ++ switch (wValue >> 8) { ++ case 0: ++ return (core_if->pwron_rxfsiz < ++ 32768) ? core_if->pwron_rxfsiz : 32768; ++ break; ++ case 1: ++ return core_if->core_params->dev_rx_fifo_size; ++ break; ++ default: ++ return -DWC_E_INVALID; ++ break; ++ } ++} ++ ++/** ++ * This function returns Tx FIFO size for IN EP ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ * @return The total of data FIFO sizes. ++ * ++ */ ++static int32_t get_txfifo_size(struct dwc_otg_pcd *pcd, uint16_t wValue) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ ++ ep = get_ep_by_addr(pcd, wValue & 0xff); ++ ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", ++ __func__, wValue & 0xff); ++ return -DWC_E_INVALID; ++ } ++ ++ if (!ep->dwc_ep.is_in) { ++ CFI_INFO ++ ("%s: No Tx FIFO assingned to the Out endpoint addr=0x%02x\n", ++ __func__, wValue & 0xff); ++ return -DWC_E_INVALID; ++ } ++ ++ switch (wValue >> 8) { ++ case 0: ++ return (GET_CORE_IF(pcd)-> ++ pwron_txfsiz[ep->dwc_ep.tx_fifo_num - 1] < ++ 768) ? GET_CORE_IF(pcd)->pwron_txfsiz[ep->dwc_ep. ++ tx_fifo_num - ++ 1] : 32768; ++ break; ++ case 1: ++ return GET_CORE_IF(pcd)->core_params->dev_tx_fifo_size[ep-> ++ dwc_ep. ++ num - 1]; ++ break; ++ default: ++ return -DWC_E_INVALID; ++ break; ++ } ++} ++ ++/** ++ * This function checks if the submitted combination of ++ * device mode FIFO sizes is possible or not. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ * @return 1 if possible, 0 otherwise. ++ * ++ */ ++static uint8_t check_fifo_sizes(dwc_otg_core_if_t * core_if) ++{ ++ uint16_t dfifo_actual = 0; ++ dwc_otg_core_params_t *params = core_if->core_params; ++ uint16_t start_addr = 0; ++ int i; ++ ++ dfifo_actual = ++ params->dev_rx_fifo_size + params->dev_nperio_tx_fifo_size; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ dfifo_actual += params->dev_tx_fifo_size[i]; ++ } ++ ++ if (dfifo_actual > core_if->total_fifo_size) { ++ return 0; ++ } ++ ++ if (params->dev_rx_fifo_size > 32768 || params->dev_rx_fifo_size < 16) ++ return 0; ++ ++ if (params->dev_nperio_tx_fifo_size > 32768 ++ || params->dev_nperio_tx_fifo_size < 16) ++ return 0; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ ++ if (params->dev_tx_fifo_size[i] > 768 ++ || params->dev_tx_fifo_size[i] < 4) ++ return 0; ++ } ++ ++ if (params->dev_rx_fifo_size > core_if->pwron_rxfsiz) ++ return 0; ++ start_addr = params->dev_rx_fifo_size; ++ ++ if (params->dev_nperio_tx_fifo_size > core_if->pwron_gnptxfsiz) ++ return 0; ++ start_addr += params->dev_nperio_tx_fifo_size; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ ++ if (params->dev_tx_fifo_size[i] > core_if->pwron_txfsiz[i]) ++ return 0; ++ start_addr += params->dev_tx_fifo_size[i]; ++ } ++ ++ return 1; ++} ++ ++/** ++ * This function resizes Device mode FIFOs ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ * @return 1 if successful, 0 otherwise ++ * ++ */ ++static uint8_t resize_fifos(dwc_otg_core_if_t * core_if) ++{ ++ int i = 0; ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ dwc_otg_core_params_t *params = core_if->core_params; ++ uint32_t rx_fifo_size; ++ fifosize_data_t nptxfifosize; ++ fifosize_data_t txfifosize[15]; ++ ++ uint32_t rx_fsz_bak; ++ uint32_t nptxfsz_bak; ++ uint32_t txfsz_bak[15]; ++ ++ uint16_t start_address; ++ uint8_t retval = 1; ++ ++ if (!check_fifo_sizes(core_if)) { ++ return 0; ++ } ++ ++ /* Configure data FIFO sizes */ ++ if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) { ++ rx_fsz_bak = dwc_read_reg32(&global_regs->grxfsiz); ++ rx_fifo_size = params->dev_rx_fifo_size; ++ dwc_write_reg32(&global_regs->grxfsiz, rx_fifo_size); ++ ++ /* ++ * Tx FIFOs These FIFOs are numbered from 1 to 15. ++ * Indexes of the FIFO size module parameters in the ++ * dev_tx_fifo_size array and the FIFO size registers in ++ * the dptxfsiz_dieptxf array run from 0 to 14. ++ */ ++ ++ /* Non-periodic Tx FIFO */ ++ nptxfsz_bak = dwc_read_reg32(&global_regs->gnptxfsiz); ++ nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size; ++ start_address = params->dev_rx_fifo_size; ++ nptxfifosize.b.startaddr = start_address; ++ ++ dwc_write_reg32(&global_regs->gnptxfsiz, nptxfifosize.d32); ++ ++ start_address += nptxfifosize.b.depth; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ txfsz_bak[i] = ++ dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i]); ++ ++ txfifosize[i].b.depth = params->dev_tx_fifo_size[i]; ++ txfifosize[i].b.startaddr = start_address; ++ dwc_write_reg32(&global_regs->dptxfsiz_dieptxf[i], ++ txfifosize[i].d32); ++ ++ start_address += txfifosize[i].b.depth; ++ } ++ ++ /** Check if register values are set correctly */ ++ if (rx_fifo_size != dwc_read_reg32(&global_regs->grxfsiz)) { ++ retval = 0; ++ } ++ ++ if (nptxfifosize.d32 != dwc_read_reg32(&global_regs->gnptxfsiz)) { ++ retval = 0; ++ } ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ if (txfifosize[i].d32 != ++ dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i])) { ++ retval = 0; ++ } ++ } ++ ++ /** If register values are not set correctly, reset old values */ ++ if (retval == 0) { ++ dwc_write_reg32(&global_regs->grxfsiz, rx_fsz_bak); ++ ++ /* Non-periodic Tx FIFO */ ++ dwc_write_reg32(&global_regs->gnptxfsiz, nptxfsz_bak); ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ dwc_write_reg32(&global_regs-> ++ dptxfsiz_dieptxf[i], ++ txfsz_bak[i]); ++ } ++ } ++ } else { ++ return 0; ++ } ++ ++ /* Flush the FIFOs */ ++ dwc_otg_flush_tx_fifo(core_if, 0x10); /* all Tx FIFOs */ ++ dwc_otg_flush_rx_fifo(core_if); ++ ++ return retval; ++} ++ ++/** ++ * This function sets a new value for the buffer Alignment setup. ++ */ ++static int cfi_ep_set_tx_fifo_val(uint8_t * buf, dwc_otg_pcd_t * pcd) ++{ ++ int retval; ++ uint32_t fsiz; ++ uint16_t size; ++ uint16_t ep_addr; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params; ++ tx_fifo_size_setup_t *ptxfifoval; ++ ++ ptxfifoval = (tx_fifo_size_setup_t *) buf; ++ ep_addr = ptxfifoval->bEndpointAddress; ++ size = ptxfifoval->wDepth; ++ ++ ep = get_ep_by_addr(pcd, ep_addr); ++ ++ CFI_INFO ++ ("%s: Set Tx FIFO size: endpoint addr=0x%02x, depth=%d, FIFO Num=%d\n", ++ __func__, ep_addr, size, ep->dwc_ep.tx_fifo_num); ++ ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint addr=0x%02x\n", ++ __func__, ep_addr); ++ return -DWC_E_INVALID; ++ } ++ ++ fsiz = params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1]; ++ params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = size; ++ ++ if (resize_fifos(GET_CORE_IF(pcd))) { ++ retval = 0; ++ } else { ++ CFI_INFO ++ ("%s: Error setting the feature Tx FIFO Size for EP%d\n", ++ __func__, ep_addr); ++ params->dev_tx_fifo_size[ep->dwc_ep.tx_fifo_num - 1] = fsiz; ++ retval = -DWC_E_INVALID; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function sets a new value for the buffer Alignment setup. ++ */ ++static int cfi_set_rx_fifo_val(uint8_t * buf, dwc_otg_pcd_t * pcd) ++{ ++ int retval; ++ uint32_t fsiz; ++ uint16_t size; ++ dwc_otg_core_params_t *params = GET_CORE_IF(pcd)->core_params; ++ rx_fifo_size_setup_t *prxfifoval; ++ ++ prxfifoval = (rx_fifo_size_setup_t *) buf; ++ size = prxfifoval->wDepth; ++ ++ fsiz = params->dev_rx_fifo_size; ++ params->dev_rx_fifo_size = size; ++ ++ if (resize_fifos(GET_CORE_IF(pcd))) { ++ retval = 0; ++ } else { ++ CFI_INFO("%s: Error setting the feature Rx FIFO Size\n", ++ __func__); ++ params->dev_rx_fifo_size = fsiz; ++ retval = -DWC_E_INVALID; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function reads the SG of an EP's buffer setup into the buffer buf ++ */ ++static int cfi_ep_get_sg_val(uint8_t * buf, struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req) ++{ ++ int retval = -DWC_E_INVALID; ++ uint8_t addr; ++ cfi_ep_t *ep; ++ ++ /* The Low Byte of the wValue contains a non-zero address of the endpoint */ ++ addr = req->wValue & 0xFF; ++ if (addr == 0) /* The address should be non-zero */ ++ return retval; ++ ++ ep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n", ++ __func__, addr); ++ return retval; ++ } ++ ++ dwc_memcpy(buf, ep->bm_sg, BS_SG_VAL_DESC_LEN); ++ retval = BS_SG_VAL_DESC_LEN; ++ return retval; ++} ++ ++/** ++ * This function reads the Concatenation value of an EP's buffer mode into ++ * the buffer buf ++ */ ++static int cfi_ep_get_concat_val(uint8_t * buf, struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req) ++{ ++ int retval = -DWC_E_INVALID; ++ uint8_t addr; ++ cfi_ep_t *ep; ++ uint8_t desc_count; ++ ++ /* The Low Byte of the wValue contains a non-zero address of the endpoint */ ++ addr = req->wValue & 0xFF; ++ if (addr == 0) /* The address should be non-zero */ ++ return retval; ++ ++ ep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n", ++ __func__, addr); ++ return retval; ++ } ++ ++ /* Copy the header to the buffer */ ++ dwc_memcpy(buf, ep->bm_concat, BS_CONCAT_VAL_HDR_LEN); ++ /* Advance the buffer pointer by the header size */ ++ buf += BS_CONCAT_VAL_HDR_LEN; ++ ++ desc_count = ep->bm_concat->hdr.bDescCount; ++ /* Copy alll the wTxBytes to the buffer */ ++ dwc_memcpy(buf, ep->bm_concat->wTxBytes, sizeof(uid16_t) * desc_count); ++ ++ retval = BS_CONCAT_VAL_HDR_LEN + sizeof(uid16_t) * desc_count; ++ return retval; ++} ++ ++/** ++ * This function reads the buffer Alignment value of an EP's buffer mode into ++ * the buffer buf ++ * ++ * @return The total number of bytes copied to the buffer or negative error code. ++ */ ++static int cfi_ep_get_align_val(uint8_t * buf, struct dwc_otg_pcd *pcd, ++ struct cfi_usb_ctrlrequest *req) ++{ ++ int retval = -DWC_E_INVALID; ++ uint8_t addr; ++ cfi_ep_t *ep; ++ ++ /* The Low Byte of the wValue contains a non-zero address of the endpoint */ ++ addr = req->wValue & 0xFF; ++ if (addr == 0) /* The address should be non-zero */ ++ return retval; ++ ++ ep = get_cfi_ep_by_addr(pcd->cfi, addr); ++ if (NULL == ep) { ++ CFI_INFO("%s: Unable to get the endpoint address(0x%02x)\n", ++ __func__, addr); ++ return retval; ++ } ++ ++ dwc_memcpy(buf, ep->bm_align, BS_ALIGN_VAL_HDR_LEN); ++ retval = BS_ALIGN_VAL_HDR_LEN; ++ ++ return retval; ++} ++ ++/** ++ * This function sets a new value for the specified feature ++ * ++ * @param pcd A pointer to the PCD object ++ * ++ * @return 0 if successful, negative error code otherwise to stall the DCE. ++ */ ++static int cfi_set_feature_value(struct dwc_otg_pcd *pcd) ++{ ++ int retval = -DWC_E_NOT_SUPPORTED; ++ uint16_t wIndex, wValue; ++ uint8_t bRequest; ++ struct dwc_otg_core_if *coreif; ++ cfiobject_t *cfi = pcd->cfi; ++ struct cfi_usb_ctrlrequest *ctrl_req; ++ uint8_t *buf; ++ ctrl_req = &cfi->ctrl_req; ++ ++ buf = pcd->cfi->ctrl_req.data; ++ ++ coreif = GET_CORE_IF(pcd); ++ bRequest = ctrl_req->bRequest; ++ wIndex = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wIndex); ++ wValue = DWC_CONSTANT_CPU_TO_LE16(ctrl_req->wValue); ++ ++ /* See which feature is to be modified */ ++ switch (wIndex) { ++ case FT_ID_DMA_BUFFER_SETUP: ++ /* Modify the feature */ ++ if ((retval = cfi_ep_set_sg_val(buf, pcd)) < 0) ++ return retval; ++ ++ /* And send this request to the gadget */ ++ cfi->need_gadget_att = 1; ++ break; ++ ++ case FT_ID_DMA_BUFF_ALIGN: ++ if ((retval = cfi_ep_set_alignment_val(buf, pcd)) < 0) ++ return retval; ++ cfi->need_gadget_att = 1; ++ break; ++ ++ case FT_ID_DMA_CONCAT_SETUP: ++ /* Modify the feature */ ++ if ((retval = cfi_ep_set_concat_val(buf, pcd)) < 0) ++ return retval; ++ cfi->need_gadget_att = 1; ++ break; ++ ++ case FT_ID_DMA_CIRCULAR: ++ CFI_INFO("FT_ID_DMA_CIRCULAR\n"); ++ break; ++ ++ case FT_ID_THRESHOLD_SETUP: ++ CFI_INFO("FT_ID_THRESHOLD_SETUP\n"); ++ break; ++ ++ case FT_ID_DFIFO_DEPTH: ++ CFI_INFO("FT_ID_DFIFO_DEPTH\n"); ++ break; ++ ++ case FT_ID_TX_FIFO_DEPTH: ++ CFI_INFO("FT_ID_TX_FIFO_DEPTH\n"); ++ if ((retval = cfi_ep_set_tx_fifo_val(buf, pcd)) < 0) ++ return retval; ++ cfi->need_gadget_att = 0; ++ break; ++ ++ case FT_ID_RX_FIFO_DEPTH: ++ CFI_INFO("FT_ID_RX_FIFO_DEPTH\n"); ++ if ((retval = cfi_set_rx_fifo_val(buf, pcd)) < 0) ++ return retval; ++ cfi->need_gadget_att = 0; ++ break; ++ } ++ ++ return retval; ++} ++ ++#endif //DWC_UTE_CFI +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_cfi.h +@@ -0,0 +1,319 @@ ++/* ========================================================================== ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_OTG_CFI_H__) ++#define __DWC_OTG_CFI_H__ ++ ++#include "dwc_otg_pcd.h" ++#include "dwc_cfi_common.h" ++ ++/** ++ * @file ++ * ++ * This file contains the CFI related OTG PCD specific common constants, interfaces ++ * (functions and macros) and data structures. ++ * ++ */ ++ ++struct dwc_otg_pcd; ++struct dwc_otg_pcd_ep; ++ ++/** OTG CFI Features (properties) ID constants */ ++/** This is a request for all Core Features */ ++#define FT_ID_DMA_MODE 0x0001 ++#define FT_ID_DMA_BUFFER_SETUP 0x0002 ++#define FT_ID_DMA_BUFF_ALIGN 0x0003 ++#define FT_ID_DMA_CONCAT_SETUP 0x0004 ++#define FT_ID_DMA_CIRCULAR 0x0005 ++#define FT_ID_THRESHOLD_SETUP 0x0006 ++#define FT_ID_DFIFO_DEPTH 0x0007 ++#define FT_ID_TX_FIFO_DEPTH 0x0008 ++#define FT_ID_RX_FIFO_DEPTH 0x0009 ++ ++/**********************************************************/ ++#define CFI_INFO_DEF ++ ++#ifdef CFI_INFO_DEF ++#define CFI_INFO(fmt...) DWC_PRINTF("CFI: " fmt); ++#else ++#define CFI_INFO(fmt...) ++#endif ++ ++#define min(x,y) ({ \ ++ x < y ? x : y; }) ++ ++#define max(x,y) ({ \ ++ x > y ? x : y; }) ++ ++/** ++ * Descriptor DMA SG Buffer setup structure (SG buffer). This structure is ++ * also used for setting up a buffer for Circular DDMA. ++ */ ++struct _ddma_sg_buffer_setup { ++#define BS_SG_VAL_DESC_LEN 6 ++ /* The OUT EP address */ ++ uint8_t bOutEndpointAddress; ++ /* The IN EP address */ ++ uint8_t bInEndpointAddress; ++ /* Number of bytes to put between transfer segments (must be DWORD boundaries) */ ++ uint8_t bOffset; ++ /* The number of transfer segments (a DMA descriptors per each segment) */ ++ uint8_t bCount; ++ /* Size (in byte) of each transfer segment */ ++ uint16_t wSize; ++} __attribute__ ((packed)); ++typedef struct _ddma_sg_buffer_setup ddma_sg_buffer_setup_t; ++ ++/** Descriptor DMA Concatenation Buffer setup structure */ ++struct _ddma_concat_buffer_setup_hdr { ++#define BS_CONCAT_VAL_HDR_LEN 4 ++ /* The endpoint for which the buffer is to be set up */ ++ uint8_t bEndpointAddress; ++ /* The count of descriptors to be used */ ++ uint8_t bDescCount; ++ /* The total size of the transfer */ ++ uint16_t wSize; ++} __attribute__ ((packed)); ++typedef struct _ddma_concat_buffer_setup_hdr ddma_concat_buffer_setup_hdr_t; ++ ++/** Descriptor DMA Concatenation Buffer setup structure */ ++struct _ddma_concat_buffer_setup { ++ /* The SG header */ ++ ddma_concat_buffer_setup_hdr_t hdr; ++ ++ /* The XFER sizes pointer (allocated dynamically) */ ++ uint16_t *wTxBytes; ++} __attribute__ ((packed)); ++typedef struct _ddma_concat_buffer_setup ddma_concat_buffer_setup_t; ++ ++/** Descriptor DMA Alignment Buffer setup structure */ ++struct _ddma_align_buffer_setup { ++#define BS_ALIGN_VAL_HDR_LEN 2 ++ uint8_t bEndpointAddress; ++ uint8_t bAlign; ++} __attribute__ ((packed)); ++typedef struct _ddma_align_buffer_setup ddma_align_buffer_setup_t; ++ ++/** Transmit FIFO Size setup structure */ ++struct _tx_fifo_size_setup { ++ uint8_t bEndpointAddress; ++ uint16_t wDepth; ++} __attribute__ ((packed)); ++typedef struct _tx_fifo_size_setup tx_fifo_size_setup_t; ++ ++/** Transmit FIFO Size setup structure */ ++struct _rx_fifo_size_setup { ++ uint16_t wDepth; ++} __attribute__ ((packed)); ++typedef struct _rx_fifo_size_setup rx_fifo_size_setup_t; ++ ++/** ++ * struct cfi_usb_ctrlrequest - the CFI implementation of the struct usb_ctrlrequest ++ * This structure encapsulates the standard usb_ctrlrequest and adds a pointer ++ * to the data returned in the data stage of a 3-stage Control Write requests. ++ */ ++struct cfi_usb_ctrlrequest { ++ uint8_t bRequestType; ++ uint8_t bRequest; ++ uint16_t wValue; ++ uint16_t wIndex; ++ uint16_t wLength; ++ uint8_t *data; ++} UPACKED; ++ ++/*---------------------------------------------------------------------------*/ ++ ++/** ++ * The CFI wrapper of the enabled and activated dwc_otg_pcd_ep structures. ++ * This structure is used to store the buffer setup data for any ++ * enabled endpoint in the PCD. ++ */ ++struct cfi_ep { ++ /* Entry for the list container */ ++ dwc_list_link_t lh; ++ /* Pointer to the active PCD endpoint structure */ ++ struct dwc_otg_pcd_ep *ep; ++ /* The last descriptor in the chain of DMA descriptors of the endpoint */ ++ struct dwc_otg_dma_desc *dma_desc_last; ++ /* The SG feature value */ ++ ddma_sg_buffer_setup_t *bm_sg; ++ /* The Circular feature value */ ++ ddma_sg_buffer_setup_t *bm_circ; ++ /* The Concatenation feature value */ ++ ddma_concat_buffer_setup_t *bm_concat; ++ /* The Alignment feature value */ ++ ddma_align_buffer_setup_t *bm_align; ++ /* XFER length */ ++ uint32_t xfer_len; ++ /* ++ * Count of DMA descriptors currently used. ++ * The total should not exceed the MAX_DMA_DESCS_PER_EP value ++ * defined in the dwc_otg_cil.h ++ */ ++ uint32_t desc_count; ++}; ++typedef struct cfi_ep cfi_ep_t; ++ ++typedef struct cfi_dma_buff { ++#define CFI_IN_BUF_LEN 1024 ++#define CFI_OUT_BUF_LEN 1024 ++ dma_addr_t addr; ++ uint8_t *buf; ++} cfi_dma_buff_t; ++ ++struct cfiobject; ++ ++/** ++ * This is the interface for the CFI operations. ++ * ++ * @param ep_enable Called when any endpoint is enabled and activated. ++ * @param release Called when the CFI object is released and it needs to correctly ++ * deallocate the dynamic memory ++ * @param ctrl_write_complete Called when the data stage of the request is complete ++ */ ++typedef struct cfi_ops { ++ int (*ep_enable) (struct cfiobject * cfi, struct dwc_otg_pcd * pcd, ++ struct dwc_otg_pcd_ep * ep); ++ void *(*ep_alloc_buf) (struct cfiobject * cfi, struct dwc_otg_pcd * pcd, ++ struct dwc_otg_pcd_ep * ep, dma_addr_t * dma, ++ unsigned size, gfp_t flags); ++ void (*release) (struct cfiobject * cfi); ++ int (*ctrl_write_complete) (struct cfiobject * cfi, ++ struct dwc_otg_pcd * pcd); ++ void (*build_descriptors) (struct cfiobject * cfi, ++ struct dwc_otg_pcd * pcd, ++ struct dwc_otg_pcd_ep * ep, ++ dwc_otg_pcd_request_t * req); ++} cfi_ops_t; ++ ++struct cfiobject { ++ cfi_ops_t ops; ++ struct dwc_otg_pcd *pcd; ++ struct usb_gadget *gadget; ++ ++ /* Buffers used to send/receive CFI-related request data */ ++ cfi_dma_buff_t buf_in; ++ cfi_dma_buff_t buf_out; ++ ++ /* CFI specific Control request wrapper */ ++ struct cfi_usb_ctrlrequest ctrl_req; ++ ++ /* The list of active EP's in the PCD of type cfi_ep_t */ ++ dwc_list_link_t active_eps; ++ ++ /* This flag shall control the propagation of a specific request ++ * to the gadget's processing routines. ++ * 0 - no gadget handling ++ * 1 - the gadget needs to know about this request (w/o completing a status ++ * phase - just return a 0 to the _setup callback) ++ */ ++ uint8_t need_gadget_att; ++ ++ /* Flag indicating whether the status IN phase needs to be ++ * completed by the PCD ++ */ ++ uint8_t need_status_in_complete; ++}; ++typedef struct cfiobject cfiobject_t; ++ ++#define DUMP_MSG ++ ++#if defined(DUMP_MSG) ++static inline void dump_msg(const u8 * buf, unsigned int length) ++{ ++ unsigned int start, num, i; ++ char line[52], *p; ++ ++ if (length >= 512) ++ return; ++ ++ start = 0; ++ while (length > 0) { ++ num = min(length, 16u); ++ p = line; ++ for (i = 0; i < num; ++i) { ++ if (i == 8) ++ *p++ = ' '; ++ DWC_SPRINTF(p, " %02x", buf[i]); ++ p += 3; ++ } ++ *p = 0; ++ DWC_DEBUG("%6x: %s\n", start, line); ++ buf += num; ++ start += num; ++ length -= num; ++ } ++} ++#else ++static inline void dump_msg(const u8 * buf, unsigned int length) ++{ ++} ++#endif ++ ++/** ++ * This function returns a pointer to cfi_ep_t object with the addr address. ++ */ ++static inline struct cfi_ep *get_cfi_ep_by_addr(struct cfiobject *cfi, ++ uint8_t addr) ++{ ++ struct cfi_ep *pcfiep; ++ dwc_list_link_t *tmp; ++ ++ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { ++ pcfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ ++ if (pcfiep->ep->desc->bEndpointAddress == addr) { ++ return pcfiep; ++ } ++ } ++ ++ return NULL; ++} ++ ++/** ++ * This function returns a pointer to cfi_ep_t object that matches ++ * the dwc_otg_pcd_ep object. ++ */ ++static inline struct cfi_ep *get_cfi_ep_by_pcd_ep(struct cfiobject *cfi, ++ struct dwc_otg_pcd_ep *ep) ++{ ++ struct cfi_ep *pcfiep = NULL; ++ dwc_list_link_t *tmp; ++ ++ DWC_LIST_FOREACH(tmp, &cfi->active_eps) { ++ pcfiep = DWC_LIST_ENTRY(tmp, struct cfi_ep, lh); ++ if (pcfiep->ep == ep) { ++ return pcfiep; ++ } ++ } ++ return NULL; ++} ++ ++int cfi_setup(struct dwc_otg_pcd *pcd, struct cfi_usb_ctrlrequest *ctrl); ++ ++#endif /* (__DWC_OTG_CFI_H__) */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_cil.c +@@ -0,0 +1,5410 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.c $ ++ * $Revision: #159 $ ++ * $Date: 2009/04/21 $ ++ * $Change: 1237465 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The Core Interface Layer provides basic services for accessing and ++ * managing the DWC_otg hardware. These services are used by both the ++ * Host Controller Driver and the Peripheral Controller Driver. ++ * ++ * The CIL manages the memory map for the core so that the HCD and PCD ++ * don't have to do this separately. It also handles basic tasks like ++ * reading/writing the registers and data FIFOs in the controller. ++ * Some of the data access functions provide encapsulation of several ++ * operations required to perform a task, such as writing multiple ++ * registers to start a transfer. Finally, the CIL performs basic ++ * services that are not specific to either the host or device modes ++ * of operation. These services include management of the OTG Host ++ * Negotiation Protocol (HNP) and Session Request Protocol (SRP). A ++ * Diagnostic API is also provided to allow testing of the controller ++ * hardware. ++ * ++ * The Core Interface Layer has the following requirements: ++ * - Provides basic controller operations. ++ * - Minimal use of OS services. ++ * - The OS services used will be abstracted by using inline functions ++ * or macros. ++ * ++ */ ++ ++#include "dwc_os.h" ++#include "dwc_otg_regs.h" ++#include "dwc_otg_cil.h" ++ ++static int dwc_otg_setup_params(dwc_otg_core_if_t * core_if); ++ ++/** ++ * This function is called to initialize the DWC_otg CSR data ++ * structures. The register addresses in the device and host ++ * structures are initialized from the base address supplied by the ++ * caller. The calling function must make the OS calls to get the ++ * base address of the DWC_otg controller registers. The core_params ++ * argument holds the parameters that specify how the core should be ++ * configured. ++ * ++ * @param reg_base_addr Base address of DWC_otg core registers ++ * ++ */ ++dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t * reg_base_addr) ++{ ++ dwc_otg_core_if_t *core_if = 0; ++ dwc_otg_dev_if_t *dev_if = 0; ++ dwc_otg_host_if_t *host_if = 0; ++ uint8_t *reg_base = (uint8_t *) reg_base_addr; ++ int i = 0; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", __func__, reg_base_addr); ++ ++ core_if = dwc_alloc(sizeof(dwc_otg_core_if_t)); ++ ++ if (core_if == 0) { ++ DWC_DEBUGPL(DBG_CIL, ++ "Allocation of dwc_otg_core_if_t failed\n"); ++ return 0; ++ } ++ core_if->core_global_regs = (dwc_otg_core_global_regs_t *) reg_base; ++ ++ /* ++ * Allocate the Device Mode structures. ++ */ ++ dev_if = dwc_alloc(sizeof(dwc_otg_dev_if_t)); ++ ++ if (dev_if == 0) { ++ DWC_DEBUGPL(DBG_CIL, "Allocation of dwc_otg_dev_if_t failed\n"); ++ dwc_free(core_if); ++ return 0; ++ } ++ ++ dev_if->dev_global_regs = ++ (dwc_otg_device_global_regs_t *) (reg_base + ++ DWC_DEV_GLOBAL_REG_OFFSET); ++ ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ dev_if->in_ep_regs[i] = (dwc_otg_dev_in_ep_regs_t *) ++ (reg_base + DWC_DEV_IN_EP_REG_OFFSET + ++ (i * DWC_EP_REG_OFFSET)); ++ ++ dev_if->out_ep_regs[i] = (dwc_otg_dev_out_ep_regs_t *) ++ (reg_base + DWC_DEV_OUT_EP_REG_OFFSET + ++ (i * DWC_EP_REG_OFFSET)); ++ DWC_DEBUGPL(DBG_CILV, "in_ep_regs[%d]->diepctl=%p\n", ++ i, &dev_if->in_ep_regs[i]->diepctl); ++ DWC_DEBUGPL(DBG_CILV, "out_ep_regs[%d]->doepctl=%p\n", ++ i, &dev_if->out_ep_regs[i]->doepctl); ++ } ++ ++ dev_if->speed = 0; // unknown ++ ++ core_if->dev_if = dev_if; ++ ++ /* ++ * Allocate the Host Mode structures. ++ */ ++ host_if = dwc_alloc(sizeof(dwc_otg_host_if_t)); ++ ++ if (host_if == 0) { ++ DWC_DEBUGPL(DBG_CIL, ++ "Allocation of dwc_otg_host_if_t failed\n"); ++ dwc_free(dev_if); ++ dwc_free(core_if); ++ return 0; ++ } ++ ++ host_if->host_global_regs = (dwc_otg_host_global_regs_t *) ++ (reg_base + DWC_OTG_HOST_GLOBAL_REG_OFFSET); ++ ++ host_if->hprt0 = ++ (uint32_t *) (reg_base + DWC_OTG_HOST_PORT_REGS_OFFSET); ++ ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ host_if->hc_regs[i] = (dwc_otg_hc_regs_t *) ++ (reg_base + DWC_OTG_HOST_CHAN_REGS_OFFSET + ++ (i * DWC_OTG_CHAN_REGS_OFFSET)); ++ DWC_DEBUGPL(DBG_CILV, "hc_reg[%d]->hcchar=%p\n", ++ i, &host_if->hc_regs[i]->hcchar); ++ } ++ ++ host_if->num_host_channels = MAX_EPS_CHANNELS; ++ core_if->host_if = host_if; ++ ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ core_if->data_fifo[i] = ++ (uint32_t *) (reg_base + DWC_OTG_DATA_FIFO_OFFSET + ++ (i * DWC_OTG_DATA_FIFO_SIZE)); ++ DWC_DEBUGPL(DBG_CILV, "data_fifo[%d]=0x%08x\n", ++ i, (unsigned)core_if->data_fifo[i]); ++ } ++ ++ core_if->pcgcctl = (uint32_t *) (reg_base + DWC_OTG_PCGCCTL_OFFSET); ++ ++ /* Initiate lx_state to L3 disconnected state */ ++ core_if->lx_state = DWC_OTG_L3; ++ /* ++ * Store the contents of the hardware configuration registers here for ++ * easy access later. ++ */ ++ core_if->hwcfg1.d32 = ++ dwc_read_reg32(&core_if->core_global_regs->ghwcfg1); ++ core_if->hwcfg2.d32 = ++ dwc_read_reg32(&core_if->core_global_regs->ghwcfg2); ++ core_if->hwcfg3.d32 = ++ dwc_read_reg32(&core_if->core_global_regs->ghwcfg3); ++ core_if->hwcfg4.d32 = ++ dwc_read_reg32(&core_if->core_global_regs->ghwcfg4); ++ ++ DWC_DEBUGPL(DBG_CILV, "hwcfg1=%08x\n", core_if->hwcfg1.d32); ++ DWC_DEBUGPL(DBG_CILV, "hwcfg2=%08x\n", core_if->hwcfg2.d32); ++ DWC_DEBUGPL(DBG_CILV, "hwcfg3=%08x\n", core_if->hwcfg3.d32); ++ DWC_DEBUGPL(DBG_CILV, "hwcfg4=%08x\n", core_if->hwcfg4.d32); ++ ++ core_if->hcfg.d32 = ++ dwc_read_reg32(&core_if->host_if->host_global_regs->hcfg); ++ core_if->dcfg.d32 = ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs->dcfg); ++ ++ DWC_DEBUGPL(DBG_CILV, "hcfg=%08x\n", core_if->hcfg.d32); ++ DWC_DEBUGPL(DBG_CILV, "dcfg=%08x\n", core_if->dcfg.d32); ++ ++ DWC_DEBUGPL(DBG_CILV, "op_mode=%0x\n", core_if->hwcfg2.b.op_mode); ++ DWC_DEBUGPL(DBG_CILV, "arch=%0x\n", core_if->hwcfg2.b.architecture); ++ DWC_DEBUGPL(DBG_CILV, "num_dev_ep=%d\n", core_if->hwcfg2.b.num_dev_ep); ++ DWC_DEBUGPL(DBG_CILV, "num_host_chan=%d\n", ++ core_if->hwcfg2.b.num_host_chan); ++ DWC_DEBUGPL(DBG_CILV, "nonperio_tx_q_depth=0x%0x\n", ++ core_if->hwcfg2.b.nonperio_tx_q_depth); ++ DWC_DEBUGPL(DBG_CILV, "host_perio_tx_q_depth=0x%0x\n", ++ core_if->hwcfg2.b.host_perio_tx_q_depth); ++ DWC_DEBUGPL(DBG_CILV, "dev_token_q_depth=0x%0x\n", ++ core_if->hwcfg2.b.dev_token_q_depth); ++ ++ DWC_DEBUGPL(DBG_CILV, "Total FIFO SZ=%d\n", ++ core_if->hwcfg3.b.dfifo_depth); ++ DWC_DEBUGPL(DBG_CILV, "xfer_size_cntr_width=%0x\n", ++ core_if->hwcfg3.b.xfer_size_cntr_width); ++ ++ /* ++ * Set the SRP sucess bit for FS-I2c ++ */ ++ core_if->srp_success = 0; ++ core_if->srp_timer_started = 0; ++ ++ /* ++ * Create new workqueue and init works ++ */ ++ core_if->wq_otg = DWC_WORKQ_ALLOC("dwc_otg"); ++ if (core_if->wq_otg == 0) { ++ DWC_WARN("DWC_WORKQ_ALLOC failed\n"); ++ dwc_free(host_if); ++ dwc_free(dev_if); ++ dwc_free(core_if); ++ return 0; ++ } ++ ++ core_if->snpsid = dwc_read_reg32(&core_if->core_global_regs->gsnpsid); ++ ++ DWC_PRINTF("Core Release: %x.%x%x%x\n", ++ (core_if->snpsid >> 12 & 0xF), ++ (core_if->snpsid >> 8 & 0xF), ++ (core_if->snpsid >> 4 & 0xF), (core_if->snpsid & 0xF)); ++ ++ core_if->wkp_timer = DWC_TIMER_ALLOC("Wake Up Timer", ++ w_wakeup_detected, core_if); ++ if (core_if->wkp_timer == 0) { ++ DWC_WARN("DWC_TIMER_ALLOC failed\n"); ++ dwc_free(host_if); ++ dwc_free(dev_if); ++ DWC_WORKQ_FREE(core_if->wq_otg); ++ dwc_free(core_if); ++ return 0; ++ } ++ ++ if (dwc_otg_setup_params(core_if)) { ++ DWC_WARN("Error while setting core params\n"); ++ } ++ ++ return core_if; ++} ++ ++/** ++ * This function frees the structures allocated by dwc_otg_cil_init(). ++ * ++ * @param core_if The core interface pointer returned from ++ * dwc_otg_cil_init(). ++ * ++ */ ++void dwc_otg_cil_remove(dwc_otg_core_if_t * core_if) ++{ ++ DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", __func__, core_if); ++ ++ /* Disable all interrupts */ ++ dwc_modify_reg32(&core_if->core_global_regs->gahbcfg, 1, 0); ++ dwc_write_reg32(&core_if->core_global_regs->gintmsk, 0); ++ ++ if (core_if->wq_otg) { ++ DWC_WORKQ_WAIT_WORK_DONE(core_if->wq_otg, 500); ++ DWC_WORKQ_FREE(core_if->wq_otg); ++ } ++ if (core_if->dev_if) { ++ dwc_free(core_if->dev_if); ++ } ++ if (core_if->host_if) { ++ dwc_free(core_if->host_if); ++ } ++ dwc_free(core_if); ++ DWC_TIMER_FREE(core_if->wkp_timer); ++ DWC_FREE(core_if->core_params); ++} ++ ++/** ++ * This function enables the controller's Global Interrupt in the AHB Config ++ * register. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_enable_global_interrupts(dwc_otg_core_if_t * core_if) ++{ ++ gahbcfg_data_t ahbcfg = {.d32 = 0 }; ++ ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */ ++ dwc_modify_reg32(&core_if->core_global_regs->gahbcfg, 0, ahbcfg.d32); ++} ++ ++/** ++ * This function disables the controller's Global Interrupt in the AHB Config ++ * register. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_disable_global_interrupts(dwc_otg_core_if_t * core_if) ++{ ++ gahbcfg_data_t ahbcfg = {.d32 = 0 }; ++ ahbcfg.b.glblintrmsk = 1; /* Enable interrupts */ ++ DWC_PRINTF("%x -> %x\n", (unsigned int)&core_if->core_global_regs->gahbcfg, ahbcfg.d32); ++ dwc_modify_reg32(&core_if->core_global_regs->gahbcfg, ahbcfg.d32, 0); ++} ++ ++/** ++ * This function initializes the commmon interrupts, used in both ++ * device and host modes. ++ * ++ * @param core_if Programming view of the DWC_otg controller ++ * ++ */ ++static void dwc_otg_enable_common_interrupts(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ /* Clear any pending OTG Interrupts */ ++ dwc_write_reg32(&global_regs->gotgint, 0xFFFFFFFF); ++ ++ /* Clear any pending interrupts */ ++ dwc_write_reg32(&global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* ++ * Enable the interrupts in the GINTMSK. ++ */ ++ intr_mask.b.modemismatch = 1; ++ intr_mask.b.otgintr = 1; ++ ++ if (!core_if->dma_enable) { ++ intr_mask.b.rxstsqlvl = 1; ++ } ++ ++ intr_mask.b.conidstschng = 1; ++ intr_mask.b.wkupintr = 1; ++ intr_mask.b.disconnect = 1; ++ intr_mask.b.usbsuspend = 1; ++ intr_mask.b.sessreqintr = 1; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ if (core_if->core_params->lpm_enable) { ++ intr_mask.b.lpmtranrcvd = 1; ++ } ++#endif ++ dwc_write_reg32(&global_regs->gintmsk, intr_mask.d32); ++} ++ ++/** ++ * Initializes the FSLSPClkSel field of the HCFG register depending on the PHY ++ * type. ++ */ ++static void init_fslspclksel(dwc_otg_core_if_t * core_if) ++{ ++ uint32_t val; ++ hcfg_data_t hcfg; ++ ++ if (((core_if->hwcfg2.b.hs_phy_type == 2) && ++ (core_if->hwcfg2.b.fs_phy_type == 1) && ++ (core_if->core_params->ulpi_fs_ls)) || ++ (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) { ++ /* Full speed PHY */ ++ val = DWC_HCFG_48_MHZ; ++ } else { ++ /* High speed PHY running at full speed or high speed */ ++ val = DWC_HCFG_30_60_MHZ; ++ } ++ ++ DWC_DEBUGPL(DBG_CIL, "Initializing HCFG.FSLSPClkSel to 0x%1x\n", val); ++ hcfg.d32 = dwc_read_reg32(&core_if->host_if->host_global_regs->hcfg); ++ hcfg.b.fslspclksel = val; ++ dwc_write_reg32(&core_if->host_if->host_global_regs->hcfg, hcfg.d32); ++} ++ ++/** ++ * Initializes the DevSpd field of the DCFG register depending on the PHY type ++ * and the enumeration speed of the device. ++ */ ++static void init_devspd(dwc_otg_core_if_t * core_if) ++{ ++ uint32_t val; ++ dcfg_data_t dcfg; ++ ++ if (((core_if->hwcfg2.b.hs_phy_type == 2) && ++ (core_if->hwcfg2.b.fs_phy_type == 1) && ++ (core_if->core_params->ulpi_fs_ls)) || ++ (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) { ++ /* Full speed PHY */ ++ val = 0x3; ++ } else if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) { ++ /* High speed PHY running at full speed */ ++ val = 0x1; ++ } else { ++ /* High speed PHY running at high speed */ ++ val = 0x0; ++ } ++ ++ DWC_DEBUGPL(DBG_CIL, "Initializing DCFG.DevSpd to 0x%1x\n", val); ++ ++ dcfg.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dcfg); ++ dcfg.b.devspd = val; ++ dwc_write_reg32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32); ++} ++ ++/** ++ * This function calculates the number of IN EPS ++ * using GHWCFG1 and GHWCFG2 registers values ++ * ++ * @param core_if Programming view of the DWC_otg controller ++ */ ++static uint32_t calc_num_in_eps(dwc_otg_core_if_t * core_if) ++{ ++ uint32_t num_in_eps = 0; ++ uint32_t num_eps = core_if->hwcfg2.b.num_dev_ep; ++ uint32_t hwcfg1 = core_if->hwcfg1.d32 >> 3; ++ uint32_t num_tx_fifos = core_if->hwcfg4.b.num_in_eps; ++ int i; ++ ++ for (i = 0; i < num_eps; ++i) { ++ if (!(hwcfg1 & 0x1)) ++ num_in_eps++; ++ ++ hwcfg1 >>= 2; ++ } ++ ++ if (core_if->hwcfg4.b.ded_fifo_en) { ++ num_in_eps = ++ (num_in_eps > num_tx_fifos) ? num_tx_fifos : num_in_eps; ++ } ++ ++ return num_in_eps; ++} ++ ++/** ++ * This function calculates the number of OUT EPS ++ * using GHWCFG1 and GHWCFG2 registers values ++ * ++ * @param core_if Programming view of the DWC_otg controller ++ */ ++static uint32_t calc_num_out_eps(dwc_otg_core_if_t * core_if) ++{ ++ uint32_t num_out_eps = 0; ++ uint32_t num_eps = core_if->hwcfg2.b.num_dev_ep; ++ uint32_t hwcfg1 = core_if->hwcfg1.d32 >> 2; ++ int i; ++ ++ for (i = 0; i < num_eps; ++i) { ++ if (!(hwcfg1 & 0x1)) ++ num_out_eps++; ++ ++ hwcfg1 >>= 2; ++ } ++ return num_out_eps; ++} ++ ++/** ++ * This function initializes the DWC_otg controller registers and ++ * prepares the core for device mode or host mode operation. ++ * ++ * @param core_if Programming view of the DWC_otg controller ++ * ++ */ ++void dwc_otg_core_init(dwc_otg_core_if_t * core_if) ++{ ++ int i = 0; ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ gahbcfg_data_t ahbcfg = {.d32 = 0 }; ++ gusbcfg_data_t usbcfg = {.d32 = 0 }; ++ gi2cctl_data_t i2cctl = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_CILV, "dwc_otg_core_init(%p) regs at %p\n", ++ core_if, global_regs); ++ ++ /* Common Initialization */ ++ ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ ++ /* Program the ULPI External VBUS bit if needed */ ++ usbcfg.b.ulpi_ext_vbus_drv = ++ (core_if->core_params->phy_ulpi_ext_vbus == ++ DWC_PHY_ULPI_EXTERNAL_VBUS) ? 1 : 0; ++ ++ /* Set external TS Dline pulsing */ ++ usbcfg.b.term_sel_dl_pulse = ++ (core_if->core_params->ts_dline == 1) ? 1 : 0; ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Reset the Controller */ ++ dwc_otg_core_reset(core_if); ++ ++ /* Initialize parameters from Hardware configuration registers. */ ++ dev_if->num_in_eps = calc_num_in_eps(core_if); ++ dev_if->num_out_eps = calc_num_out_eps(core_if); ++ ++ DWC_DEBUGPL(DBG_CIL, "num_dev_perio_in_ep=%d\n", ++ core_if->hwcfg4.b.num_dev_perio_in_ep); ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; i++) { ++ dev_if->perio_tx_fifo_size[i] = ++ dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i]) >> 16; ++ DWC_DEBUGPL(DBG_CIL, "Periodic Tx FIFO SZ #%d=0x%0x\n", ++ i, dev_if->perio_tx_fifo_size[i]); ++ } ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ dev_if->tx_fifo_size[i] = ++ dwc_read_reg32(&global_regs->dptxfsiz_dieptxf[i]) >> 16; ++ DWC_DEBUGPL(DBG_CIL, "Tx FIFO SZ #%d=0x%0x\n", ++ i, dev_if->perio_tx_fifo_size[i]); ++ } ++ ++ core_if->total_fifo_size = core_if->hwcfg3.b.dfifo_depth; ++ core_if->rx_fifo_size = dwc_read_reg32(&global_regs->grxfsiz); ++ core_if->nperio_tx_fifo_size = ++ dwc_read_reg32(&global_regs->gnptxfsiz) >> 16; ++ ++ DWC_DEBUGPL(DBG_CIL, "Total FIFO SZ=%d\n", core_if->total_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "Rx FIFO SZ=%d\n", core_if->rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO SZ=%d\n", ++ core_if->nperio_tx_fifo_size); ++ ++ /* This programming sequence needs to happen in FS mode before any other ++ * programming occurs */ ++ if ((core_if->core_params->speed == DWC_SPEED_PARAM_FULL) && ++ (core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS)) { ++ /* If FS mode with FS PHY */ ++ ++ /* core_init() is now called on every switch so only call the ++ * following for the first time through. */ ++ if (!core_if->phy_init_done) { ++ core_if->phy_init_done = 1; ++ DWC_DEBUGPL(DBG_CIL, "FS_PHY detected\n"); ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ usbcfg.b.physel = 1; ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Reset after a PHY select */ ++ dwc_otg_core_reset(core_if); ++ } ++ ++ /* Program DCFG.DevSpd or HCFG.FSLSPclkSel to 48Mhz in FS. Also ++ * do this on HNP Dev/Host mode switches (done in dev_init and ++ * host_init). */ ++ if (dwc_otg_is_host_mode(core_if)) { ++ init_fslspclksel(core_if); ++ } else { ++ init_devspd(core_if); ++ } ++ ++ if (core_if->core_params->i2c_enable) { ++ DWC_DEBUGPL(DBG_CIL, "FS_PHY Enabling I2c\n"); ++ /* Program GUSBCFG.OtgUtmifsSel to I2C */ ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ usbcfg.b.otgutmifssel = 1; ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ ++ /* Program GI2CCTL.I2CEn */ ++ i2cctl.d32 = dwc_read_reg32(&global_regs->gi2cctl); ++ i2cctl.b.i2cdevaddr = 1; ++ i2cctl.b.i2cen = 0; ++ dwc_write_reg32(&global_regs->gi2cctl, i2cctl.d32); ++ i2cctl.b.i2cen = 1; ++ dwc_write_reg32(&global_regs->gi2cctl, i2cctl.d32); ++ } ++ ++ } /* endif speed == DWC_SPEED_PARAM_FULL */ ++ else { ++ /* High speed PHY. */ ++ if (!core_if->phy_init_done) { ++ core_if->phy_init_done = 1; ++ /* HS PHY parameters. These parameters are preserved ++ * during soft reset so only program the first time. Do ++ * a soft reset immediately after setting phyif. */ ++ usbcfg.b.ulpi_utmi_sel = core_if->core_params->phy_type; ++ if (usbcfg.b.ulpi_utmi_sel == 1) { ++ /* ULPI interface */ ++ usbcfg.b.phyif = 0; ++ usbcfg.b.ddrsel = ++ core_if->core_params->phy_ulpi_ddr; ++ } else { ++ /* UTMI+ interface */ ++ if (core_if->core_params->phy_utmi_width == 16) { ++ usbcfg.b.phyif = 1; ++ ++ } else { ++ usbcfg.b.phyif = 0; ++ } ++ ++ } ++ ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ /* Reset after setting the PHY parameters */ ++ dwc_otg_core_reset(core_if); ++ } ++ } ++ ++ if ((core_if->hwcfg2.b.hs_phy_type == 2) && ++ (core_if->hwcfg2.b.fs_phy_type == 1) && ++ (core_if->core_params->ulpi_fs_ls)) { ++ DWC_DEBUGPL(DBG_CIL, "Setting ULPI FSLS\n"); ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ usbcfg.b.ulpi_fsls = 1; ++ usbcfg.b.ulpi_clk_sus_m = 1; ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ } else { ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ usbcfg.b.ulpi_fsls = 0; ++ usbcfg.b.ulpi_clk_sus_m = 0; ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ } ++ ++ /* Program the GAHBCFG Register. */ ++ switch (core_if->hwcfg2.b.architecture) { ++ ++ case DWC_SLAVE_ONLY_ARCH: ++ DWC_DEBUGPL(DBG_CIL, "Slave Only Mode\n"); ++ ahbcfg.b.nptxfemplvl_txfemplvl = ++ DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY; ++ ahbcfg.b.ptxfemplvl = DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY; ++ core_if->dma_enable = 0; ++ core_if->dma_desc_enable = 0; ++ break; ++ ++ case DWC_EXT_DMA_ARCH: ++ DWC_DEBUGPL(DBG_CIL, "External DMA Mode\n"); ++ { ++ uint8_t brst_sz = core_if->core_params->dma_burst_size; ++ ahbcfg.b.hburstlen = 0; ++ while (brst_sz > 1) { ++ ahbcfg.b.hburstlen++; ++ brst_sz >>= 1; ++ } ++ } ++ core_if->dma_enable = (core_if->core_params->dma_enable != 0); ++ core_if->dma_desc_enable = ++ (core_if->core_params->dma_desc_enable != 0); ++ break; ++ ++ case DWC_INT_DMA_ARCH: ++ DWC_DEBUGPL(DBG_CIL, "Internal DMA Mode\n"); ++ /*ahbcfg.b.hburstlen = DWC_GAHBCFG_INT_DMA_BURST_INCR; */ ++ ahbcfg.b.hburstlen = (1<<3)|(0<<0); /* WRESP=1, max 4 beats */ ++ core_if->dma_enable = (core_if->core_params->dma_enable != 0); ++ core_if->dma_desc_enable = ++ (core_if->core_params->dma_desc_enable != 0); ++ break; ++ ++ } ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable) { ++ DWC_PRINTF("Using Descriptor DMA mode\n"); ++ } else { ++ DWC_PRINTF("Using Buffer DMA mode\n"); ++ ++ } ++ } else { ++ DWC_PRINTF("Using Slave mode\n"); ++ core_if->dma_desc_enable = 0; ++ } ++ ++ ahbcfg.b.dmaenable = core_if->dma_enable; ++ dwc_write_reg32(&global_regs->gahbcfg, ahbcfg.d32); ++ ++ core_if->en_multiple_tx_fifo = core_if->hwcfg4.b.ded_fifo_en; ++ ++ core_if->pti_enh_enable = core_if->core_params->pti_enable != 0; ++ core_if->multiproc_int_enable = core_if->core_params->mpi_enable; ++ DWC_PRINTF("Periodic Transfer Interrupt Enhancement - %s\n", ++ ((core_if->pti_enh_enable) ? "enabled" : "disabled")); ++ DWC_PRINTF("Multiprocessor Interrupt Enhancement - %s\n", ++ ((core_if->multiproc_int_enable) ? "enabled" : "disabled")); ++ ++ /* ++ * Program the GUSBCFG register. ++ */ ++ usbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ ++ switch (core_if->hwcfg2.b.op_mode) { ++ case DWC_MODE_HNP_SRP_CAPABLE: ++ usbcfg.b.hnpcap = (core_if->core_params->otg_cap == ++ DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE); ++ usbcfg.b.srpcap = (core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_SRP_ONLY_CAPABLE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = (core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_NO_HNP_SRP_CAPABLE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ break; ++ ++ case DWC_MODE_SRP_CAPABLE_DEVICE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = (core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_NO_SRP_CAPABLE_DEVICE: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ break; ++ ++ case DWC_MODE_SRP_CAPABLE_HOST: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = (core_if->core_params->otg_cap != ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ break; ++ ++ case DWC_MODE_NO_SRP_CAPABLE_HOST: ++ usbcfg.b.hnpcap = 0; ++ usbcfg.b.srpcap = 0; ++ break; ++ } ++ ++ dwc_write_reg32(&global_regs->gusbcfg, usbcfg.d32); ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ if (core_if->core_params->lpm_enable) { ++ glpmcfg_data_t lpmcfg = {.d32 = 0 }; ++ ++ /* To enable LPM support set lpm_cap_en bit */ ++ lpmcfg.b.lpm_cap_en = 1; ++ ++ /* Make AppL1Res ACK */ ++ lpmcfg.b.appl_resp = 1; ++ ++ /* Retry 3 times */ ++ lpmcfg.b.retry_count = 3; ++ ++ dwc_modify_reg32(&core_if->core_global_regs->glpmcfg, ++ 0, lpmcfg.d32); ++ ++ } ++#endif ++ if (core_if->core_params->ic_usb_cap) { ++ gusbcfg_data_t gusbcfg = {.d32 = 0 }; ++ gusbcfg.b.ic_usb_cap = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gusbcfg, ++ 0, gusbcfg.d32); ++ } ++ ++ /* Enable common interrupts */ ++ dwc_otg_enable_common_interrupts(core_if); ++ ++ /* Do device or host intialization based on mode during PCD ++ * and HCD initialization */ ++ if (dwc_otg_is_host_mode(core_if)) { ++ DWC_DEBUGPL(DBG_ANY, "Host Mode\n"); ++ core_if->op_state = A_HOST; ++ } else { ++ DWC_DEBUGPL(DBG_ANY, "Device Mode\n"); ++ core_if->op_state = B_PERIPHERAL; ++#ifdef DWC_DEVICE_ONLY ++ dwc_otg_core_dev_init(core_if); ++#endif ++ } ++} ++ ++/** ++ * This function enables the Device mode interrupts. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ */ ++void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t * core_if) ++{ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ ++ DWC_DEBUGPL(DBG_CIL, "%s()\n", __func__); ++ ++ /* Disable all interrupts. */ ++ dwc_write_reg32(&global_regs->gintmsk, 0); ++ ++ /* Clear any pending interrupts */ ++ dwc_write_reg32(&global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Enable the common interrupts */ ++ dwc_otg_enable_common_interrupts(core_if); ++ ++ /* Enable interrupts */ ++ intr_mask.b.usbreset = 1; ++ intr_mask.b.enumdone = 1; ++ ++ if (!core_if->multiproc_int_enable) { ++ intr_mask.b.inepintr = 1; ++ intr_mask.b.outepintr = 1; ++ } ++ ++ intr_mask.b.erlysuspend = 1; ++ ++ if (core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.epmismatch = 1; ++ } ++#ifdef DWC_EN_ISOC ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable == 0) { ++ if (core_if->pti_enh_enable) { ++ dctl_data_t dctl = {.d32 = 0 }; ++ dctl.b.ifrmnum = 1; ++ dwc_modify_reg32(&core_if->dev_if-> ++ dev_global_regs->dctl, 0, ++ dctl.d32); ++ } else { ++ intr_mask.b.incomplisoin = 1; ++ intr_mask.b.incomplisoout = 1; ++ } ++ } ++ } else { ++ intr_mask.b.incomplisoin = 1; ++ intr_mask.b.incomplisoout = 1; ++ } ++#endif /* DWC_EN_ISOC */ ++ ++ /** @todo NGS: Should this be a module parameter? */ ++#ifdef USE_PERIODIC_EP ++ intr_mask.b.isooutdrop = 1; ++ intr_mask.b.eopframe = 1; ++ intr_mask.b.incomplisoin = 1; ++ intr_mask.b.incomplisoout = 1; ++#endif ++ ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, "%s() gintmsk=%0x\n", __func__, ++ dwc_read_reg32(&global_regs->gintmsk)); ++} ++ ++/** ++ * This function initializes the DWC_otg controller registers for ++ * device mode. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ */ ++void dwc_otg_core_dev_init(dwc_otg_core_if_t * core_if) ++{ ++ int i; ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dwc_otg_core_params_t *params = core_if->core_params; ++ dcfg_data_t dcfg = {.d32 = 0 }; ++ grstctl_t resetctl = {.d32 = 0 }; ++ uint32_t rx_fifo_size; ++ fifosize_data_t nptxfifosize; ++ fifosize_data_t txfifosize; ++ dthrctl_data_t dthrctl; ++ fifosize_data_t ptxfifosize; ++ ++ /* Restart the Phy Clock */ ++ dwc_write_reg32(core_if->pcgcctl, 0); ++ ++ /* Device configuration register */ ++ init_devspd(core_if); ++ dcfg.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dcfg); ++ dcfg.b.descdma = (core_if->dma_desc_enable) ? 1 : 0; ++ dcfg.b.perfrint = DWC_DCFG_FRAME_INTERVAL_80; ++ ++ dwc_write_reg32(&dev_if->dev_global_regs->dcfg, dcfg.d32); ++ ++ /* Configure data FIFO sizes */ ++ if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) { ++ DWC_DEBUGPL(DBG_CIL, "Total FIFO Size=%d\n", ++ core_if->total_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "Rx FIFO Size=%d\n", ++ params->dev_rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO Size=%d\n", ++ params->dev_nperio_tx_fifo_size); ++ ++ /* Rx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial grxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->grxfsiz)); ++ ++#ifdef DWC_UTE_CFI ++ core_if->pwron_rxfsiz = dwc_read_reg32(&global_regs->grxfsiz); ++ core_if->init_rxfsiz = params->dev_rx_fifo_size; ++#endif ++ rx_fifo_size = params->dev_rx_fifo_size; ++ dwc_write_reg32(&global_regs->grxfsiz, rx_fifo_size); ++ ++ DWC_DEBUGPL(DBG_CIL, "new grxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->grxfsiz)); ++ ++ /** Set Periodic Tx FIFO Mask all bits 0 */ ++ core_if->p_tx_msk = 0; ++ ++ /** Set Tx FIFO Mask all bits 0 */ ++ core_if->tx_msk = 0; ++ ++ if (core_if->en_multiple_tx_fifo == 0) { ++ /* Non-periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ ++ nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size; ++ nptxfifosize.b.startaddr = params->dev_rx_fifo_size; ++ ++ dwc_write_reg32(&global_regs->gnptxfsiz, ++ nptxfifosize.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ ++ /**@todo NGS: Fix Periodic FIFO Sizing! */ ++ /* ++ * Periodic Tx FIFOs These FIFOs are numbered from 1 to 15. ++ * Indexes of the FIFO size module parameters in the ++ * dev_perio_tx_fifo_size array and the FIFO size registers in ++ * the dptxfsiz array run from 0 to 14. ++ */ ++ /** @todo Finish debug of this */ ++ ptxfifosize.b.startaddr = ++ nptxfifosize.b.startaddr + nptxfifosize.b.depth; ++ for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; ++ i++) { ++ ptxfifosize.b.depth = ++ params->dev_perio_tx_fifo_size[i]; ++ DWC_DEBUGPL(DBG_CIL, ++ "initial dptxfsiz_dieptxf[%d]=%08x\n", ++ i, ++ dwc_read_reg32(&global_regs-> ++ dptxfsiz_dieptxf ++ [i])); ++ dwc_write_reg32(&global_regs-> ++ dptxfsiz_dieptxf[i], ++ ptxfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL, ++ "new dptxfsiz_dieptxf[%d]=%08x\n", ++ i, ++ dwc_read_reg32(&global_regs-> ++ dptxfsiz_dieptxf ++ [i])); ++ ptxfifosize.b.startaddr += ptxfifosize.b.depth; ++ } ++ } else { ++ /* ++ * Tx FIFOs These FIFOs are numbered from 1 to 15. ++ * Indexes of the FIFO size module parameters in the ++ * dev_tx_fifo_size array and the FIFO size registers in ++ * the dptxfsiz_dieptxf array run from 0 to 14. ++ */ ++ ++ /* Non-periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ ++#ifdef DWC_UTE_CFI ++ core_if->pwron_gnptxfsiz = ++ (dwc_read_reg32(&global_regs->gnptxfsiz) >> 16); ++ core_if->init_gnptxfsiz = ++ params->dev_nperio_tx_fifo_size; ++#endif ++ nptxfifosize.b.depth = params->dev_nperio_tx_fifo_size; ++ nptxfifosize.b.startaddr = params->dev_rx_fifo_size; ++ ++ dwc_write_reg32(&global_regs->gnptxfsiz, ++ nptxfifosize.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ ++ txfifosize.b.startaddr = ++ nptxfifosize.b.startaddr + nptxfifosize.b.depth; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; i++) { ++ ++ txfifosize.b.depth = ++ params->dev_tx_fifo_size[i]; ++ ++ DWC_DEBUGPL(DBG_CIL, ++ "initial dptxfsiz_dieptxf[%d]=%08x\n", ++ i, ++ dwc_read_reg32(&global_regs-> ++ dptxfsiz_dieptxf ++ [i])); ++ ++#ifdef DWC_UTE_CFI ++ core_if->pwron_txfsiz[i] = ++ (dwc_read_reg32 ++ (&global_regs->dptxfsiz_dieptxf[i]) >> 16); ++ core_if->init_txfsiz[i] = ++ params->dev_tx_fifo_size[i]; ++#endif ++ dwc_write_reg32(&global_regs-> ++ dptxfsiz_dieptxf[i], ++ txfifosize.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, ++ "new dptxfsiz_dieptxf[%d]=%08x\n", ++ i, ++ dwc_read_reg32(&global_regs-> ++ dptxfsiz_dieptxf ++ [i])); ++ ++ txfifosize.b.startaddr += txfifosize.b.depth; ++ } ++ } ++ } ++ /* Flush the FIFOs */ ++ dwc_otg_flush_tx_fifo(core_if, 0x10); /* all Tx FIFOs */ ++ dwc_otg_flush_rx_fifo(core_if); ++ ++ /* Flush the Learning Queue. */ ++ resetctl.b.intknqflsh = 1; ++ dwc_write_reg32(&core_if->core_global_regs->grstctl, resetctl.d32); ++ ++ /* Clear all pending Device Interrupts */ ++ /** @todo - if the condition needed to be checked ++ * or in any case all pending interrutps should be cleared? ++ */ ++ if (core_if->multiproc_int_enable) { ++ for (i = 0; i < core_if->dev_if->num_in_eps; ++i) { ++ dwc_write_reg32(&dev_if->dev_global_regs-> ++ diepeachintmsk[i], 0); ++ } ++ ++ for (i = 0; i < core_if->dev_if->num_out_eps; ++i) { ++ dwc_write_reg32(&dev_if->dev_global_regs-> ++ doepeachintmsk[i], 0); ++ } ++ ++ dwc_write_reg32(&dev_if->dev_global_regs->deachint, 0xFFFFFFFF); ++ dwc_write_reg32(&dev_if->dev_global_regs->deachintmsk, 0); ++ } else { ++ dwc_write_reg32(&dev_if->dev_global_regs->diepmsk, 0); ++ dwc_write_reg32(&dev_if->dev_global_regs->doepmsk, 0); ++ dwc_write_reg32(&dev_if->dev_global_regs->daint, 0xFFFFFFFF); ++ dwc_write_reg32(&dev_if->dev_global_regs->daintmsk, 0); ++ } ++ ++ for (i = 0; i <= dev_if->num_in_eps; i++) { ++ depctl_data_t depctl; ++ depctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[i]->diepctl); ++ if (depctl.b.epena) { ++ depctl.d32 = 0; ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ } else { ++ depctl.d32 = 0; ++ } ++ ++ dwc_write_reg32(&dev_if->in_ep_regs[i]->diepctl, depctl.d32); ++ ++ dwc_write_reg32(&dev_if->in_ep_regs[i]->dieptsiz, 0); ++ dwc_write_reg32(&dev_if->in_ep_regs[i]->diepdma, 0); ++ dwc_write_reg32(&dev_if->in_ep_regs[i]->diepint, 0xFF); ++ } ++ ++ for (i = 0; i <= dev_if->num_out_eps; i++) { ++ depctl_data_t depctl; ++ depctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[i]->doepctl); ++ if (depctl.b.epena) { ++ depctl.d32 = 0; ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ } else { ++ depctl.d32 = 0; ++ } ++ ++ dwc_write_reg32(&dev_if->out_ep_regs[i]->doepctl, depctl.d32); ++ ++ dwc_write_reg32(&dev_if->out_ep_regs[i]->doeptsiz, 0); ++ dwc_write_reg32(&dev_if->out_ep_regs[i]->doepdma, 0); ++ dwc_write_reg32(&dev_if->out_ep_regs[i]->doepint, 0xFF); ++ } ++ ++ if (core_if->en_multiple_tx_fifo && core_if->dma_enable) { ++ dev_if->non_iso_tx_thr_en = params->thr_ctl & 0x1; ++ dev_if->iso_tx_thr_en = (params->thr_ctl >> 1) & 0x1; ++ dev_if->rx_thr_en = (params->thr_ctl >> 2) & 0x1; ++ ++ dev_if->rx_thr_length = params->rx_thr_length; ++ dev_if->tx_thr_length = params->tx_thr_length; ++ ++ dev_if->setup_desc_index = 0; ++ ++ dthrctl.d32 = 0; ++ dthrctl.b.non_iso_thr_en = dev_if->non_iso_tx_thr_en; ++ dthrctl.b.iso_thr_en = dev_if->iso_tx_thr_en; ++ dthrctl.b.tx_thr_len = dev_if->tx_thr_length; ++ dthrctl.b.rx_thr_en = dev_if->rx_thr_en; ++ dthrctl.b.rx_thr_len = dev_if->rx_thr_length; ++ dthrctl.b.ahb_thr_ratio = params->ahb_thr_ratio; ++ ++ dwc_write_reg32(&dev_if->dev_global_regs->dtknqr3_dthrctl, ++ dthrctl.d32); ++ ++ DWC_DEBUGPL(DBG_CIL, ++ "Non ISO Tx Thr - %d\nISO Tx Thr - %d\nRx Thr - %d\nTx Thr Len - %d\nRx Thr Len - %d\n", ++ dthrctl.b.non_iso_thr_en, dthrctl.b.iso_thr_en, ++ dthrctl.b.rx_thr_en, dthrctl.b.tx_thr_len, ++ dthrctl.b.rx_thr_len); ++ ++ } ++ ++ dwc_otg_enable_device_interrupts(core_if); ++ ++ { ++ diepmsk_data_t msk = {.d32 = 0 }; ++ msk.b.txfifoundrn = 1; ++ if (core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&dev_if->dev_global_regs-> ++ diepeachintmsk[0], msk.d32, msk.d32); ++ } else { ++ dwc_modify_reg32(&dev_if->dev_global_regs->diepmsk, ++ msk.d32, msk.d32); ++ } ++ } ++ ++ if (core_if->multiproc_int_enable) { ++ /* Set NAK on Babble */ ++ dctl_data_t dctl = {.d32 = 0 }; ++ dctl.b.nakonbble = 1; ++ dwc_modify_reg32(&dev_if->dev_global_regs->dctl, 0, dctl.d32); ++ } ++} ++ ++/** ++ * This function enables the Host mode interrupts. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ */ ++void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_CIL, "%s(%p)\n", __func__, core_if); ++ ++ /* Disable all interrupts. */ ++ dwc_write_reg32(&global_regs->gintmsk, 0); ++ ++ /* Clear any pending interrupts. */ ++ dwc_write_reg32(&global_regs->gintsts, 0xFFFFFFFF); ++ ++ /* Enable the common interrupts */ ++ dwc_otg_enable_common_interrupts(core_if); ++ ++ /* ++ * Enable host mode interrupts without disturbing common ++ * interrupts. ++ */ ++ ++ /* Do not need sof interrupt for Descriptor DMA*/ ++ if (!core_if->dma_desc_enable) ++ intr_mask.b.sofintr = 1; ++ intr_mask.b.portintr = 1; ++ intr_mask.b.hcintr = 1; ++ ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, intr_mask.d32); ++} ++ ++/** ++ * This function disables the Host Mode interrupts. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ */ ++void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s()\n", __func__); ++ ++ /* ++ * Disable host mode interrupts without disturbing common ++ * interrupts. ++ */ ++ intr_mask.b.sofintr = 1; ++ intr_mask.b.portintr = 1; ++ intr_mask.b.hcintr = 1; ++ intr_mask.b.ptxfempty = 1; ++ intr_mask.b.nptxfempty = 1; ++ ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, 0); ++} ++ ++/** ++ * This function initializes the DWC_otg controller registers for ++ * host mode. ++ * ++ * This function flushes the Tx and Rx FIFOs and it flushes any entries in the ++ * request queues. Host channels are reset to ensure that they are ready for ++ * performing transfers. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * ++ */ ++void dwc_otg_core_host_init(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ dwc_otg_host_if_t *host_if = core_if->host_if; ++ dwc_otg_core_params_t *params = core_if->core_params; ++ hprt0_data_t hprt0 = {.d32 = 0 }; ++ fifosize_data_t nptxfifosize; ++ fifosize_data_t ptxfifosize; ++ int i; ++ hcchar_data_t hcchar; ++ hcfg_data_t hcfg; ++ dwc_otg_hc_regs_t *hc_regs; ++ int num_channels; ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s(%p)\n", __func__, core_if); ++ ++ /* Restart the Phy Clock */ ++ dwc_write_reg32(core_if->pcgcctl, 0); ++ ++ /* Initialize Host Configuration Register */ ++ init_fslspclksel(core_if); ++ if (core_if->core_params->speed == DWC_SPEED_PARAM_FULL) { ++ hcfg.d32 = dwc_read_reg32(&host_if->host_global_regs->hcfg); ++ hcfg.b.fslssupp = 1; ++ dwc_write_reg32(&host_if->host_global_regs->hcfg, hcfg.d32); ++ ++ } ++ ++ if (core_if->core_params->dma_desc_enable) { ++ uint8_t op_mode = core_if->hwcfg2.b.op_mode; ++ if (!(core_if->hwcfg4.b.desc_dma && (core_if->snpsid >= OTG_CORE_REV_2_90a) && ++ ((op_mode == DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) || ++ (op_mode == DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) || ++ (op_mode == DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG) || ++ (op_mode == DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST) || ++ (op_mode == DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST)))) { ++ ++ DWC_ERROR("Host can't operate in Descriptor DMA mode.\n" ++ "Either core version is below 2.90a or " ++ "GHWCFG2, GHWCFG4 registers' values do not allow Descriptor DMA in host mode.\n" ++ "To run the driver in Buffer DMA host mode set dma_desc_enable " ++ "module parameter to 0.\n"); ++ return; ++ } ++ hcfg.d32 = dwc_read_reg32(&host_if->host_global_regs->hcfg); ++ hcfg.b.descdma = 1; ++ dwc_write_reg32(&host_if->host_global_regs->hcfg, hcfg.d32); ++ } ++ ++ /* Configure data FIFO sizes */ ++ if (core_if->hwcfg2.b.dynamic_fifo && params->enable_dynamic_fifo) { ++ DWC_DEBUGPL(DBG_CIL, "Total FIFO Size=%d\n", ++ core_if->total_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "Rx FIFO Size=%d\n", ++ params->host_rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "NP Tx FIFO Size=%d\n", ++ params->host_nperio_tx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "P Tx FIFO Size=%d\n", ++ params->host_perio_tx_fifo_size); ++ ++ /* Rx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial grxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->grxfsiz)); ++ dwc_write_reg32(&global_regs->grxfsiz, ++ params->host_rx_fifo_size); ++ DWC_DEBUGPL(DBG_CIL, "new grxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->grxfsiz)); ++ ++ /* Non-periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ nptxfifosize.b.depth = params->host_nperio_tx_fifo_size; ++ nptxfifosize.b.startaddr = params->host_rx_fifo_size; ++ dwc_write_reg32(&global_regs->gnptxfsiz, nptxfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL, "new gnptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->gnptxfsiz)); ++ ++ /* Periodic Tx FIFO */ ++ DWC_DEBUGPL(DBG_CIL, "initial hptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->hptxfsiz)); ++ ptxfifosize.b.depth = params->host_perio_tx_fifo_size; ++ ptxfifosize.b.startaddr = ++ nptxfifosize.b.startaddr + nptxfifosize.b.depth; ++ dwc_write_reg32(&global_regs->hptxfsiz, ptxfifosize.d32); ++ DWC_DEBUGPL(DBG_CIL, "new hptxfsiz=%08x\n", ++ dwc_read_reg32(&global_regs->hptxfsiz)); ++ } ++ ++ /* Clear Host Set HNP Enable in the OTG Control Register */ ++ gotgctl.b.hstsethnpen = 1; ++ dwc_modify_reg32(&global_regs->gotgctl, gotgctl.d32, 0); ++ ++ /* Make sure the FIFOs are flushed. */ ++ dwc_otg_flush_tx_fifo(core_if, 0x10 /* all Tx FIFOs */ ); ++ dwc_otg_flush_rx_fifo(core_if); ++ ++ if(!core_if->core_params->dma_desc_enable) { ++ /* Flush out any leftover queued requests. */ ++ num_channels = core_if->core_params->host_channels; ++ ++ for (i = 0; i < num_channels; i++) { ++ hc_regs = core_if->host_if->hc_regs[i]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.chen = 0; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ } ++ ++ /* Halt all channels to put them into a known state. */ ++ for (i = 0; i < num_channels; i++) { ++ int count = 0; ++ hc_regs = core_if->host_if->hc_regs[i]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ DWC_DEBUGPL(DBG_HCDV, "%s: Halt channel %d regs %p\n", __func__, i, hc_regs); ++ do { ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (++count > 1000) { ++ DWC_ERROR ++ ("%s: Unable to clear halt on channel %d (timeout HCCHAR 0x%X @%p)\n", ++ __func__, i, hcchar.d32, &hc_regs->hcchar); ++ break; ++ } ++ dwc_udelay(1); ++ } while (hcchar.b.chen); ++ } ++ } ++ ++ /* Turn on the vbus power. */ ++ DWC_PRINTF("Init: Port Power? op_state=%d\n", core_if->op_state); ++ if (core_if->op_state == A_HOST) { ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ DWC_PRINTF("Init: Power Port (%d)\n", hprt0.b.prtpwr); ++ if (hprt0.b.prtpwr == 0) { ++ hprt0.b.prtpwr = 1; ++ dwc_write_reg32(host_if->hprt0, hprt0.d32); ++ } ++ } ++ ++ dwc_otg_enable_host_interrupts(core_if); ++} ++ ++/** ++ * Prepares a host channel for transferring packets to/from a specific ++ * endpoint. The HCCHARn register is set up with the characteristics specified ++ * in _hc. Host channel interrupts that may need to be serviced while this ++ * transfer is in progress are enabled. ++ * ++ * @param core_if Programming view of DWC_otg controller ++ * @param hc Information needed to initialize the host channel ++ */ ++void dwc_otg_hc_init(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ uint32_t intr_enable; ++ hcintmsk_data_t hc_intr_mask; ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ ++ uint8_t hc_num = hc->hc_num; ++ dwc_otg_host_if_t *host_if = core_if->host_if; ++ dwc_otg_hc_regs_t *hc_regs = host_if->hc_regs[hc_num]; ++ ++ /* Clear old interrupt conditions for this host channel. */ ++ hc_intr_mask.d32 = 0xFFFFFFFF; ++ hc_intr_mask.b.reserved14_31 = 0; ++ dwc_write_reg32(&hc_regs->hcint, hc_intr_mask.d32); ++ ++ /* Enable channel interrupts required for this transfer. */ ++ hc_intr_mask.d32 = 0; ++ hc_intr_mask.b.chhltd = 1; ++ if (core_if->dma_enable) { ++ /* For Descriptor DMA mode core halts the channel on AHB error. Interrupt is not required */ ++ if (!core_if->dma_desc_enable) ++ hc_intr_mask.b.ahberr = 1; ++ else { ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) ++ hc_intr_mask.b.xfercompl = 1; ++ } ++ ++ if (hc->error_state && !hc->do_split && ++ hc->ep_type != DWC_OTG_EP_TYPE_ISOC) { ++ hc_intr_mask.b.ack = 1; ++ if (hc->ep_is_in) { ++ hc_intr_mask.b.datatglerr = 1; ++ if (hc->ep_type != DWC_OTG_EP_TYPE_INTR) { ++ hc_intr_mask.b.nak = 1; ++ } ++ } ++ } ++ } else { ++ switch (hc->ep_type) { ++ case DWC_OTG_EP_TYPE_CONTROL: ++ case DWC_OTG_EP_TYPE_BULK: ++ hc_intr_mask.b.xfercompl = 1; ++ hc_intr_mask.b.stall = 1; ++ hc_intr_mask.b.xacterr = 1; ++ hc_intr_mask.b.datatglerr = 1; ++ if (hc->ep_is_in) { ++ hc_intr_mask.b.bblerr = 1; ++ } else { ++ hc_intr_mask.b.nak = 1; ++ hc_intr_mask.b.nyet = 1; ++ if (hc->do_ping) { ++ hc_intr_mask.b.ack = 1; ++ } ++ } ++ ++ if (hc->do_split) { ++ hc_intr_mask.b.nak = 1; ++ if (hc->complete_split) { ++ hc_intr_mask.b.nyet = 1; ++ } else { ++ hc_intr_mask.b.ack = 1; ++ } ++ } ++ ++ if (hc->error_state) { ++ hc_intr_mask.b.ack = 1; ++ } ++ break; ++ case DWC_OTG_EP_TYPE_INTR: ++ hc_intr_mask.b.xfercompl = 1; ++ hc_intr_mask.b.nak = 1; ++ hc_intr_mask.b.stall = 1; ++ hc_intr_mask.b.xacterr = 1; ++ hc_intr_mask.b.datatglerr = 1; ++ hc_intr_mask.b.frmovrun = 1; ++ ++ if (hc->ep_is_in) { ++ hc_intr_mask.b.bblerr = 1; ++ } ++ if (hc->error_state) { ++ hc_intr_mask.b.ack = 1; ++ } ++ if (hc->do_split) { ++ if (hc->complete_split) { ++ hc_intr_mask.b.nyet = 1; ++ } else { ++ hc_intr_mask.b.ack = 1; ++ } ++ } ++ break; ++ case DWC_OTG_EP_TYPE_ISOC: ++ hc_intr_mask.b.xfercompl = 1; ++ hc_intr_mask.b.frmovrun = 1; ++ hc_intr_mask.b.ack = 1; ++ ++ if (hc->ep_is_in) { ++ hc_intr_mask.b.xacterr = 1; ++ hc_intr_mask.b.bblerr = 1; ++ } ++ break; ++ } ++ } ++ dwc_write_reg32(&hc_regs->hcintmsk, hc_intr_mask.d32); ++ ++ /* Enable the top level host channel interrupt. */ ++ intr_enable = (1 << hc_num); ++ dwc_modify_reg32(&host_if->host_global_regs->haintmsk, 0, intr_enable); ++ ++ /* Make sure host channel interrupts are enabled. */ ++ gintmsk.b.hcintr = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, 0, gintmsk.d32); ++ ++ /* ++ * Program the HCCHARn register with the endpoint characteristics for ++ * the current transfer. ++ */ ++ hcchar.d32 = 0; ++ hcchar.b.devaddr = hc->dev_addr; ++ hcchar.b.epnum = hc->ep_num; ++ hcchar.b.epdir = hc->ep_is_in; ++ hcchar.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW); ++ hcchar.b.eptype = hc->ep_type; ++ hcchar.b.mps = hc->max_packet; ++ ++ dwc_write_reg32(&host_if->hc_regs[hc_num]->hcchar, hcchar.d32); ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " Dev Addr: %d\n", hcchar.b.devaddr); ++ DWC_DEBUGPL(DBG_HCDV, " Ep Num: %d\n", hcchar.b.epnum); ++ DWC_DEBUGPL(DBG_HCDV, " Is In: %d\n", hcchar.b.epdir); ++ DWC_DEBUGPL(DBG_HCDV, " Is Low Speed: %d\n", hcchar.b.lspddev); ++ DWC_DEBUGPL(DBG_HCDV, " Ep Type: %d\n", hcchar.b.eptype); ++ DWC_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n", hcchar.b.mps); ++ DWC_DEBUGPL(DBG_HCDV, " Multi Cnt: %d\n", hcchar.b.multicnt); ++ ++ /* ++ * Program the HCSPLIT register for SPLITs ++ */ ++ hcsplt.d32 = 0; ++ if (hc->do_split) { ++ DWC_DEBUGPL(DBG_HCDV, "Programming HC %d with split --> %s\n", ++ hc->hc_num, ++ hc->complete_split ? "CSPLIT" : "SSPLIT"); ++ hcsplt.b.compsplt = hc->complete_split; ++ hcsplt.b.xactpos = hc->xact_pos; ++ hcsplt.b.hubaddr = hc->hub_addr; ++ hcsplt.b.prtaddr = hc->port_addr; ++ DWC_DEBUGPL(DBG_HCDV, " comp split %d\n", hc->complete_split); ++ DWC_DEBUGPL(DBG_HCDV, " xact pos %d\n", hc->xact_pos); ++ DWC_DEBUGPL(DBG_HCDV, " hub addr %d\n", hc->hub_addr); ++ DWC_DEBUGPL(DBG_HCDV, " port addr %d\n", hc->port_addr); ++ DWC_DEBUGPL(DBG_HCDV, " is_in %d\n", hc->ep_is_in); ++ DWC_DEBUGPL(DBG_HCDV, " Max Pkt: %d\n", hcchar.b.mps); ++ DWC_DEBUGPL(DBG_HCDV, " xferlen: %d\n", hc->xfer_len); ++ } ++ dwc_write_reg32(&host_if->hc_regs[hc_num]->hcsplt, hcsplt.d32); ++ ++} ++ ++/** ++ * Attempts to halt a host channel. This function should only be called in ++ * Slave mode or to abort a transfer in either Slave mode or DMA mode. Under ++ * normal circumstances in DMA mode, the controller halts the channel when the ++ * transfer is complete or a condition occurs that requires application ++ * intervention. ++ * ++ * In slave mode, checks for a free request queue entry, then sets the Channel ++ * Enable and Channel Disable bits of the Host Channel Characteristics ++ * register of the specified channel to intiate the halt. If there is no free ++ * request queue entry, sets only the Channel Disable bit of the HCCHARn ++ * register to flush requests for this channel. In the latter case, sets a ++ * flag to indicate that the host channel needs to be halted when a request ++ * queue slot is open. ++ * ++ * In DMA mode, always sets the Channel Enable and Channel Disable bits of the ++ * HCCHARn register. The controller ensures there is space in the request ++ * queue before submitting the halt request. ++ * ++ * Some time may elapse before the core flushes any posted requests for this ++ * host channel and halts. The Channel Halted interrupt handler completes the ++ * deactivation of the host channel. ++ * ++ * @param core_if Controller register interface. ++ * @param hc Host channel to halt. ++ * @param halt_status Reason for halting the channel. ++ */ ++void dwc_otg_hc_halt(dwc_otg_core_if_t * core_if, ++ dwc_hc_t * hc, dwc_otg_halt_status_e halt_status) ++{ ++ gnptxsts_data_t nptxsts; ++ hptxsts_data_t hptxsts; ++ hcchar_data_t hcchar; ++ dwc_otg_hc_regs_t *hc_regs; ++ dwc_otg_core_global_regs_t *global_regs; ++ dwc_otg_host_global_regs_t *host_global_regs; ++ ++ DWC_DEBUGPL(DBG_HW2937, " dwc_otg_hc_halt(%d)\n", hc->hc_num); ++ hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ global_regs = core_if->core_global_regs; ++ host_global_regs = core_if->host_if->host_global_regs; ++ ++ DWC_ASSERT(!(halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS), ++ "halt_status = %d\n", halt_status); ++ ++ if (halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE || ++ halt_status == DWC_OTG_HC_XFER_AHB_ERR) { ++ /* ++ * Disable all channel interrupts except Ch Halted. The QTD ++ * and QH state associated with this transfer has been cleared ++ * (in the case of URB_DEQUEUE), so the channel needs to be ++ * shut down carefully to prevent crashes. ++ */ ++ hcintmsk_data_t hcintmsk; ++ hcintmsk.d32 = 0; ++ hcintmsk.b.chhltd = 1; ++ dwc_write_reg32(&hc_regs->hcintmsk, hcintmsk.d32); ++ ++ /* ++ * Make sure no other interrupts besides halt are currently ++ * pending. Handling another interrupt could cause a crash due ++ * to the QTD and QH state. ++ */ ++ dwc_write_reg32(&hc_regs->hcint, ~hcintmsk.d32); ++ ++ /* ++ * Make sure the halt status is set to URB_DEQUEUE or AHB_ERR ++ * even if the channel was already halted for some other ++ * reason. ++ */ ++ hc->halt_status = halt_status; ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen == 0) { ++ /* ++ * The channel is either already halted or it hasn't ++ * started yet. In DMA mode, the transfer may halt if ++ * it finishes normally or a condition occurs that ++ * requires driver intervention. Don't want to halt ++ * the channel again. In either Slave or DMA mode, ++ * it's possible that the transfer has been assigned ++ * to a channel, but not started yet when an URB is ++ * dequeued. Don't want to halt a channel that hasn't ++ * started yet. ++ */ ++ return; ++ } ++ } ++ if (hc->halt_pending) { ++ /* ++ * A halt has already been issued for this channel. This might ++ * happen when a transfer is aborted by a higher level in ++ * the stack. ++ */ ++#ifdef DEBUG ++ DWC_PRINTF ++ ("*** %s: Channel %d, _hc->halt_pending already set ***\n", ++ __func__, hc->hc_num); ++ ++#endif ++ return; ++ } ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ ++ /* No need to set the bit in DDMA for disabling the channel */ ++ //TODO check it everywhere channel is disabled ++ if(!core_if->core_params->dma_desc_enable) ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 1; ++ ++ if (!core_if->dma_enable) { ++ /* Check for space in the request queue to issue the halt. */ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL || ++ hc->ep_type == DWC_OTG_EP_TYPE_BULK) { ++ nptxsts.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ if (nptxsts.b.nptxqspcavail == 0) { ++ hcchar.b.chen = 0; ++ } ++ } else { ++ hptxsts.d32 = ++ dwc_read_reg32(&host_global_regs->hptxsts); ++ if ((hptxsts.b.ptxqspcavail == 0) ++ || (core_if->queuing_high_bandwidth)) { ++ hcchar.b.chen = 0; ++ } ++ } ++ } ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ hc->halt_status = halt_status; ++ ++ if (hcchar.b.chen) { ++ hc->halt_pending = 1; ++ hc->halt_on_queue = 0; ++ } else { ++ hc->halt_on_queue = 1; ++ } ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " hcchar: 0x%08x\n", hcchar.d32); ++ DWC_DEBUGPL(DBG_HCDV, " halt_pending: %d\n", hc->halt_pending); ++ DWC_DEBUGPL(DBG_HCDV, " halt_on_queue: %d\n", hc->halt_on_queue); ++ DWC_DEBUGPL(DBG_HCDV, " halt_status: %d\n", hc->halt_status); ++ ++ return; ++} ++ ++/** ++ * Clears the transfer state for a host channel. This function is normally ++ * called after a transfer is done and the host channel is being released. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param hc Identifies the host channel to clean up. ++ */ ++void dwc_otg_hc_cleanup(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ dwc_otg_hc_regs_t *hc_regs; ++ ++ hc->xfer_started = 0; ++ ++ /* ++ * Clear channel interrupt enables and any unhandled channel interrupt ++ * conditions. ++ */ ++ hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ dwc_write_reg32(&hc_regs->hcintmsk, 0); ++ dwc_write_reg32(&hc_regs->hcint, 0xFFFFFFFF); ++#ifdef DEBUG ++ DWC_TIMER_CANCEL(core_if->hc_xfer_timer[hc->hc_num]); ++#endif ++} ++ ++/** ++ * Sets the channel property that indicates in which frame a periodic transfer ++ * should occur. This is always set to the _next_ frame. This function has no ++ * effect on non-periodic transfers. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param hc Identifies the host channel to set up and its properties. ++ * @param hcchar Current value of the HCCHAR register for the specified host ++ * channel. ++ */ ++static inline void hc_set_even_odd_frame(dwc_otg_core_if_t * core_if, ++ dwc_hc_t * hc, hcchar_data_t * hcchar) ++{ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ hfnum_data_t hfnum; ++ hfnum.d32 = ++ dwc_read_reg32(&core_if->host_if->host_global_regs->hfnum); ++ ++ /* 1 if _next_ frame is odd, 0 if it's even */ ++ hcchar->b.oddfrm = (hfnum.b.frnum & 0x1) ? 0 : 1; ++#ifdef DEBUG ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR && hc->do_split ++ && !hc->complete_split) { ++ switch (hfnum.b.frnum & 0x7) { ++ case 7: ++ core_if->hfnum_7_samples++; ++ core_if->hfnum_7_frrem_accum += hfnum.b.frrem; ++ break; ++ case 0: ++ core_if->hfnum_0_samples++; ++ core_if->hfnum_0_frrem_accum += hfnum.b.frrem; ++ break; ++ default: ++ core_if->hfnum_other_samples++; ++ core_if->hfnum_other_frrem_accum += ++ hfnum.b.frrem; ++ break; ++ } ++ } ++#endif ++ } ++} ++ ++#ifdef DEBUG ++void hc_xfer_timeout(void *ptr) ++{ ++ hc_xfer_info_t *xfer_info = (hc_xfer_info_t *) ptr; ++ int hc_num = xfer_info->hc->hc_num; ++ DWC_WARN("%s: timeout on channel %d\n", __func__, hc_num); ++ DWC_WARN(" start_hcchar_val 0x%08x\n", ++ xfer_info->core_if->start_hcchar_val[hc_num]); ++} ++#endif ++ ++void set_pid_isoc(dwc_hc_t * hc) ++{ ++ /* Set up the initial PID for the transfer. */ ++ if (hc->speed == DWC_OTG_EP_SPEED_HIGH) { ++ if (hc->ep_is_in) { ++ if (hc->multi_count == 1) { ++ hc->data_pid_start = ++ DWC_OTG_HC_PID_DATA0; ++ } else if (hc->multi_count == 2) { ++ hc->data_pid_start = ++ DWC_OTG_HC_PID_DATA1; ++ } else { ++ hc->data_pid_start = ++ DWC_OTG_HC_PID_DATA2; ++ } ++ } else { ++ if (hc->multi_count == 1) { ++ hc->data_pid_start = ++ DWC_OTG_HC_PID_DATA0; ++ } else { ++ hc->data_pid_start = ++ DWC_OTG_HC_PID_MDATA; ++ } ++ } ++ } else { ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA0; ++ } ++} ++ ++/** ++ * This function does the setup for a data transfer for a host channel and ++ * starts the transfer. May be called in either Slave mode or DMA mode. In ++ * Slave mode, the caller must ensure that there is sufficient space in the ++ * request queue and Tx Data FIFO. ++ * ++ * For an OUT transfer in Slave mode, it loads a data packet into the ++ * appropriate FIFO. If necessary, additional data packets will be loaded in ++ * the Host ISR. ++ * ++ * For an IN transfer in Slave mode, a data packet is requested. The data ++ * packets are unloaded from the Rx FIFO in the Host ISR. If necessary, ++ * additional data packets are requested in the Host ISR. ++ * ++ * For a PING transfer in Slave mode, the Do Ping bit is set in the HCTSIZ ++ * register along with a packet count of 1 and the channel is enabled. This ++ * causes a single PING transaction to occur. Other fields in HCTSIZ are ++ * simply set to 0 since no data transfer occurs in this case. ++ * ++ * For a PING transfer in DMA mode, the HCTSIZ register is initialized with ++ * all the information required to perform the subsequent data transfer. In ++ * addition, the Do Ping bit is set in the HCTSIZ register. In this case, the ++ * controller performs the entire PING protocol, then starts the data ++ * transfer. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param hc Information needed to initialize the host channel. The xfer_len ++ * value may be reduced to accommodate the max widths of the XferSize and ++ * PktCnt fields in the HCTSIZn register. The multi_count value may be changed ++ * to reflect the final xfer_len value. ++ */ ++void dwc_otg_hc_start_transfer(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ uint16_t num_packets; ++ uint32_t max_hc_xfer_size = core_if->core_params->max_transfer_size; ++ uint16_t max_hc_pkt_count = core_if->core_params->max_packet_count; ++ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ ++ hctsiz.d32 = 0; ++ ++ if (hc->do_ping) { ++ if (!core_if->dma_enable) { ++ dwc_otg_hc_do_ping(core_if, hc); ++ hc->xfer_started = 1; ++ return; ++ } else { ++ hctsiz.b.dopng = 1; ++ } ++ } ++ ++ if (hc->do_split) { ++ num_packets = 1; ++ ++ if (hc->complete_split && !hc->ep_is_in) { ++ /* For CSPLIT OUT Transfer, set the size to 0 so the ++ * core doesn't expect any data written to the FIFO */ ++ hc->xfer_len = 0; ++ } else if (hc->ep_is_in || (hc->xfer_len > hc->max_packet)) { ++ hc->xfer_len = hc->max_packet; ++ } else if (!hc->ep_is_in && (hc->xfer_len > 188)) { ++ hc->xfer_len = 188; ++ } ++ ++ hctsiz.b.xfersize = hc->xfer_len; ++ } else { ++ /* ++ * Ensure that the transfer length and packet count will fit ++ * in the widths allocated for them in the HCTSIZn register. ++ */ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * Make sure the transfer size is no larger than one ++ * (micro)frame's worth of data. (A check was done ++ * when the periodic transfer was accepted to ensure ++ * that a (micro)frame's worth of data can be ++ * programmed into a channel.) ++ */ ++ uint32_t max_periodic_len = ++ hc->multi_count * hc->max_packet; ++ if (hc->xfer_len > max_periodic_len) { ++ hc->xfer_len = max_periodic_len; ++ } else { ++ } ++ } else if (hc->xfer_len > max_hc_xfer_size) { ++ /* Make sure that xfer_len is a multiple of max packet size. */ ++ hc->xfer_len = max_hc_xfer_size - hc->max_packet + 1; ++ } ++ ++ if (hc->xfer_len > 0) { ++ num_packets = ++ (hc->xfer_len + hc->max_packet - ++ 1) / hc->max_packet; ++ if (num_packets > max_hc_pkt_count) { ++ num_packets = max_hc_pkt_count; ++ hc->xfer_len = num_packets * hc->max_packet; ++ } ++ } else { ++ /* Need 1 packet for transfer length of 0. */ ++ num_packets = 1; ++ } ++ ++ if (hc->ep_is_in) { ++ /* Always program an integral # of max packets for IN transfers. */ ++ hc->xfer_len = num_packets * hc->max_packet; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * Make sure that the multi_count field matches the ++ * actual transfer length. ++ */ ++ hc->multi_count = num_packets; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) ++ set_pid_isoc(hc); ++ ++ hctsiz.b.xfersize = hc->xfer_len; ++ } ++ ++ hc->start_pkt_count = num_packets; ++ hctsiz.b.pktcnt = num_packets; ++ hctsiz.b.pid = hc->data_pid_start; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " Xfer Size: %d\n", hctsiz.b.xfersize); ++ DWC_DEBUGPL(DBG_HCDV, " Num Pkts: %d\n", hctsiz.b.pktcnt); ++ DWC_DEBUGPL(DBG_HCDV, " Start PID: %d\n", hctsiz.b.pid); ++ ++ if (core_if->dma_enable) { ++ dwc_dma_t dma_addr; ++ if (hc->align_buff) { ++ dma_addr = hc->align_buff; ++ } else { ++ dma_addr = (uint32_t)hc->xfer_buff; ++ } ++ dwc_write_reg32(&hc_regs->hcdma, dma_addr); ++ } ++ ++ /* Start the split */ ++ if (hc->do_split) { ++ hcsplt_data_t hcsplt; ++ hcsplt.d32 = dwc_read_reg32(&hc_regs->hcsplt); ++ hcsplt.b.spltena = 1; ++ dwc_write_reg32(&hc_regs->hcsplt, hcsplt.d32); ++ } ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.multicnt = hc->multi_count; ++ hc_set_even_odd_frame(core_if, hc, &hcchar); ++#ifdef DEBUG ++ core_if->start_hcchar_val[hc->hc_num] = hcchar.d32; ++ if (hcchar.b.chdis) { ++ DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n", ++ __func__, hc->hc_num, hcchar.d32); ++ } ++#endif ++ ++ /* Set host channel enable after all other setup is complete. */ ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ hc->xfer_started = 1; ++ hc->requests++; ++ ++ if (!core_if->dma_enable && !hc->ep_is_in && hc->xfer_len > 0) { ++ /* Load OUT packet into the appropriate Tx FIFO. */ ++ dwc_otg_hc_write_packet(core_if, hc); ++ } ++#ifdef DEBUG ++ if (hc->ep_type != DWC_OTG_EP_TYPE_INTR) { ++ DWC_DEBUGPL(DBG_HCDV, "transfer %d from core_if %p\n", ++ hc->hc_num, core_if);//GRAYG ++ core_if->hc_xfer_info[hc->hc_num].core_if = core_if; ++ core_if->hc_xfer_info[hc->hc_num].hc = hc; ++ /* Start a timer for this transfer. */ ++ DWC_TIMER_SCHEDULE(core_if->hc_xfer_timer[hc->hc_num], 10000); ++ } ++#endif ++} ++ ++/** ++ * This function does the setup for a data transfer for a host channel ++ * and starts the transfer in Descriptor DMA mode. ++ * ++ * Initializes HCTSIZ register. For a PING transfer the Do Ping bit is set. ++ * Sets PID and NTD values. For periodic transfers ++ * initializes SCHED_INFO field with micro-frame bitmap. ++ * ++ * Initializes HCDMA register with descriptor list address and CTD value ++ * then starts the transfer via enabling the channel. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param hc Information needed to initialize the host channel. ++ */ ++void dwc_otg_hc_start_transfer_ddma(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ hcdma_data_t hcdma; ++ ++ hctsiz.d32 = 0; ++ ++ if (hc->do_ping && !hc->ep_is_in) ++ hctsiz.b_ddma.dopng = 1; ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) ++ set_pid_isoc(hc); ++ ++ /* Packet Count and Xfer Size are not used in Descriptor DMA mode */ ++ hctsiz.b_ddma.pid = hc->data_pid_start; ++ hctsiz.b_ddma.ntd = hc->ntd - 1; /* 0 - 1 descriptor, 1 - 2 descriptors, etc. */ ++ hctsiz.b_ddma.schinfo = hc->schinfo; /* Non-zero only for high-speed interrupt endpoints */ ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " Start PID: %d\n", hctsiz.b.pid); ++ DWC_DEBUGPL(DBG_HCDV, " NTD: %d\n", hctsiz.b_ddma.ntd); ++ ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ hcdma.d32 = 0; ++ hcdma.b.dma_addr = ((uint32_t)hc->desc_list_addr) >> 11; ++ ++ /* Always start from first descriptor. */ ++ hcdma.b.ctd = 0; ++ dwc_write_reg32(&hc_regs->hcdma, hcdma.d32); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.multicnt = hc->multi_count; ++ ++#ifdef DEBUG ++ core_if->start_hcchar_val[hc->hc_num] = hcchar.d32; ++ if (hcchar.b.chdis) { ++ DWC_WARN("%s: chdis set, channel %d, hcchar 0x%08x\n", ++ __func__, hc->hc_num, hcchar.d32); ++ } ++#endif ++ ++ /* Set host channel enable after all other setup is complete. */ ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ hc->xfer_started = 1; ++ hc->requests++; ++ ++#ifdef DEBUG ++ if ((hc->ep_type != DWC_OTG_EP_TYPE_INTR) && (hc->ep_type != DWC_OTG_EP_TYPE_ISOC)) { ++ DWC_DEBUGPL(DBG_HCDV, "DMA transfer %d from core_if %p\n", ++ hc->hc_num, core_if);//GRAYG ++ core_if->hc_xfer_info[hc->hc_num].core_if = core_if; ++ core_if->hc_xfer_info[hc->hc_num].hc = hc; ++ /* Start a timer for this transfer. */ ++ DWC_TIMER_SCHEDULE(core_if->hc_xfer_timer[hc->hc_num], 10000); ++ } ++ ++#endif ++ ++} ++ ++/** ++ * This function continues a data transfer that was started by previous call ++ * to <code>dwc_otg_hc_start_transfer</code>. The caller must ensure there is ++ * sufficient space in the request queue and Tx Data FIFO. This function ++ * should only be called in Slave mode. In DMA mode, the controller acts ++ * autonomously to complete transfers programmed to a host channel. ++ * ++ * For an OUT transfer, a new data packet is loaded into the appropriate FIFO ++ * if there is any data remaining to be queued. For an IN transfer, another ++ * data packet is always requested. For the SETUP phase of a control transfer, ++ * this function does nothing. ++ * ++ * @return 1 if a new request is queued, 0 if no more requests are required ++ * for this transfer. ++ */ ++int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ ++ if (hc->do_split) { ++ /* SPLITs always queue just once per channel */ ++ return 0; ++ } else if (hc->data_pid_start == DWC_OTG_HC_PID_SETUP) { ++ /* SETUPs are queued only once since they can't be NAKed. */ ++ return 0; ++ } else if (hc->ep_is_in) { ++ /* ++ * Always queue another request for other IN transfers. If ++ * back-to-back INs are issued and NAKs are received for both, ++ * the driver may still be processing the first NAK when the ++ * second NAK is received. When the interrupt handler clears ++ * the NAK interrupt for the first NAK, the second NAK will ++ * not be seen. So we can't depend on the NAK interrupt ++ * handler to requeue a NAKed request. Instead, IN requests ++ * are issued each time this function is called. When the ++ * transfer completes, the extra requests for the channel will ++ * be flushed. ++ */ ++ hcchar_data_t hcchar; ++ dwc_otg_hc_regs_t *hc_regs = ++ core_if->host_if->hc_regs[hc->hc_num]; ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hc_set_even_odd_frame(core_if, hc, &hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ DWC_DEBUGPL(DBG_HCDV, " IN xfer: hcchar = 0x%08x\n", ++ hcchar.d32); ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ hc->requests++; ++ return 1; ++ } else { ++ /* OUT transfers. */ ++ if (hc->xfer_count < hc->xfer_len) { ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ hcchar_data_t hcchar; ++ dwc_otg_hc_regs_t *hc_regs; ++ hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hc_set_even_odd_frame(core_if, hc, &hcchar); ++ } ++ ++ /* Load OUT packet into the appropriate Tx FIFO. */ ++ dwc_otg_hc_write_packet(core_if, hc); ++ hc->requests++; ++ return 1; ++ } else { ++ return 0; ++ } ++ } ++} ++ ++/** ++ * Starts a PING transfer. This function should only be called in Slave mode. ++ * The Do Ping bit is set in the HCTSIZ register, then the channel is enabled. ++ */ ++void dwc_otg_hc_do_ping(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ dwc_otg_hc_regs_t *hc_regs = core_if->host_if->hc_regs[hc->hc_num]; ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s: Channel %d\n", __func__, hc->hc_num); ++ ++ hctsiz.d32 = 0; ++ hctsiz.b.dopng = 1; ++ hctsiz.b.pktcnt = 1; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.chen = 1; ++ hcchar.b.chdis = 0; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++} ++ ++/* ++ * This function writes a packet into the Tx FIFO associated with the Host ++ * Channel. For a channel associated with a non-periodic EP, the non-periodic ++ * Tx FIFO is written. For a channel associated with a periodic EP, the ++ * periodic Tx FIFO is written. This function should only be called in Slave ++ * mode. ++ * ++ * Upon return the xfer_buff and xfer_count fields in _hc are incremented by ++ * then number of bytes written to the Tx FIFO. ++ */ ++void dwc_otg_hc_write_packet(dwc_otg_core_if_t * core_if, dwc_hc_t * hc) ++{ ++ uint32_t i; ++ uint32_t remaining_count; ++ uint32_t byte_count; ++ uint32_t dword_count; ++ ++ uint32_t *data_buff = (uint32_t *) (hc->xfer_buff); ++ uint32_t *data_fifo = core_if->data_fifo[hc->hc_num]; ++ ++ remaining_count = hc->xfer_len - hc->xfer_count; ++ if (remaining_count > hc->max_packet) { ++ byte_count = hc->max_packet; ++ } else { ++ byte_count = remaining_count; ++ } ++ ++ dword_count = (byte_count + 3) / 4; ++ ++ if ((((unsigned long)data_buff) & 0x3) == 0) { ++ /* xfer_buff is DWORD aligned. */ ++ for (i = 0; i < dword_count; i++, data_buff++) { ++ dwc_write_reg32(data_fifo, *data_buff); ++ } ++ } else { ++ /* xfer_buff is not DWORD aligned. */ ++ for (i = 0; i < dword_count; i++, data_buff++) { ++ uint32_t data; ++ data = ++ (data_buff[0] | data_buff[1] << 8 | data_buff[2] << ++ 16 | data_buff[3] << 24); ++ dwc_write_reg32(data_fifo, data); ++ } ++ } ++ ++ hc->xfer_count += byte_count; ++ hc->xfer_buff += byte_count; ++} ++ ++/** ++ * Gets the current USB frame number. This is the frame number from the last ++ * SOF packet. ++ */ ++uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t * core_if) ++{ ++ dsts_data_t dsts; ++ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ /* read current frame/microframe number from DSTS register */ ++ return dsts.b.soffn; ++} ++ ++/** ++ * This function reads a setup packet from the Rx FIFO into the destination ++ * buffer. This function is called from the Rx Status Queue Level (RxStsQLvl) ++ * Interrupt routine when a SETUP packet has been received in Slave mode. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dest Destination buffer for packet data. ++ */ ++void dwc_otg_read_setup_packet(dwc_otg_core_if_t * core_if, uint32_t * dest) ++{ ++ /* Get the 8 bytes of a setup transaction data */ ++ ++ /* Pop 2 DWORDS off the receive data FIFO into memory */ ++ dest[0] = dwc_read_reg32(core_if->data_fifo[0]); ++ dest[1] = dwc_read_reg32(core_if->data_fifo[0]); ++} ++ ++/** ++ * This function enables EP0 OUT to receive SETUP packets and configures EP0 ++ * IN for transmitting packets. It is normally called when the ++ * "Enumeration Done" interrupt occurs. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP0 data. ++ */ ++void dwc_otg_ep0_activate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dsts_data_t dsts; ++ depctl_data_t diepctl; ++ depctl_data_t doepctl; ++ dctl_data_t dctl = {.d32 = 0 }; ++ ++ /* Read the Device Status and Endpoint 0 Control registers */ ++ dsts.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dsts); ++ diepctl.d32 = dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl); ++ doepctl.d32 = dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl); ++ ++ /* Set the MPS of the IN EP based on the enumeration speed */ ++ switch (dsts.b.enumspd) { ++ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: ++ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: ++ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ: ++ diepctl.b.mps = DWC_DEP0CTL_MPS_64; ++ break; ++ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ: ++ diepctl.b.mps = DWC_DEP0CTL_MPS_8; ++ break; ++ } ++ ++ dwc_write_reg32(&dev_if->in_ep_regs[0]->diepctl, diepctl.d32); ++ ++ /* Enable OUT EP for receive */ ++ doepctl.b.epena = 1; ++ dwc_write_reg32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32); ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, "doepctl0=%0x\n", ++ dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl)); ++ DWC_DEBUGPL(DBG_PCDV, "diepctl0=%0x\n", ++ dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl)); ++#endif ++ dctl.b.cgnpinnak = 1; ++ ++ dwc_modify_reg32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); ++ DWC_DEBUGPL(DBG_PCDV, "dctl=%0x\n", ++ dwc_read_reg32(&dev_if->dev_global_regs->dctl)); ++} ++ ++/** ++ * This function activates an EP. The Device EP control register for ++ * the EP is configured as defined in the ep structure. Note: This ++ * function is not used for EP0. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to activate. ++ */ ++void dwc_otg_ep_activate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ depctl_data_t depctl; ++ volatile uint32_t *addr; ++ daint_data_t daintmsk = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s() EP%d-%s\n", __func__, ep->num, ++ (ep->is_in ? "IN" : "OUT")); ++ ++ /* Read DEPCTLn register */ ++ if (ep->is_in == 1) { ++ addr = &dev_if->in_ep_regs[ep->num]->diepctl; ++ daintmsk.ep.in = 1 << ep->num; ++ } else { ++ addr = &dev_if->out_ep_regs[ep->num]->doepctl; ++ daintmsk.ep.out = 1 << ep->num; ++ } ++ ++ /* If the EP is already active don't change the EP Control ++ * register. */ ++ depctl.d32 = dwc_read_reg32(addr); ++ if (!depctl.b.usbactep) { ++ depctl.b.mps = ep->maxpacket; ++ depctl.b.eptype = ep->type; ++ depctl.b.txfnum = ep->tx_fifo_num; ++ ++ if (ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ depctl.b.setd0pid = 1; // ??? ++ } else { ++ depctl.b.setd0pid = 1; ++ } ++ depctl.b.usbactep = 1; ++ ++ dwc_write_reg32(addr, depctl.d32); ++ DWC_DEBUGPL(DBG_PCDV, "DEPCTL=%08x\n", dwc_read_reg32(addr)); ++ } ++ ++ /* Enable the Interrupt for this EP */ ++ if (core_if->multiproc_int_enable) { ++ if (ep->is_in == 1) { ++ diepmsk_data_t diepmsk = {.d32 = 0 }; ++ diepmsk.b.xfercompl = 1; ++ diepmsk.b.timeout = 1; ++ diepmsk.b.epdisabled = 1; ++ diepmsk.b.ahberr = 1; ++ diepmsk.b.intknepmis = 1; ++ diepmsk.b.txfifoundrn = 1; //????? ++ ++ if (core_if->dma_desc_enable) { ++ diepmsk.b.bna = 1; ++ } ++/* ++ if(core_if->dma_enable) { ++ doepmsk.b.nak = 1; ++ } ++*/ ++ dwc_write_reg32(&dev_if->dev_global_regs-> ++ diepeachintmsk[ep->num], diepmsk.d32); ++ ++ } else { ++ doepmsk_data_t doepmsk = {.d32 = 0 }; ++ doepmsk.b.xfercompl = 1; ++ doepmsk.b.ahberr = 1; ++ doepmsk.b.epdisabled = 1; ++ ++ if (core_if->dma_desc_enable) { ++ doepmsk.b.bna = 1; ++ } ++/* ++ doepmsk.b.babble = 1; ++ doepmsk.b.nyet = 1; ++ doepmsk.b.nak = 1; ++*/ ++ dwc_write_reg32(&dev_if->dev_global_regs-> ++ doepeachintmsk[ep->num], doepmsk.d32); ++ } ++ dwc_modify_reg32(&dev_if->dev_global_regs->deachintmsk, ++ 0, daintmsk.d32); ++ } else { ++ dwc_modify_reg32(&dev_if->dev_global_regs->daintmsk, ++ 0, daintmsk.d32); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "DAINTMSK=%0x\n", ++ dwc_read_reg32(&dev_if->dev_global_regs->daintmsk)); ++ ++ ep->stall_clear_flag = 0; ++ return; ++} ++ ++/** ++ * This function deactivates an EP. This is done by clearing the USB Active ++ * EP bit in the Device EP control register. Note: This function is not used ++ * for EP0. EP0 cannot be deactivated. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to deactivate. ++ */ ++void dwc_otg_ep_deactivate(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl = {.d32 = 0 }; ++ volatile uint32_t *addr; ++ daint_data_t daintmsk = {.d32 = 0 }; ++ ++ /* Read DEPCTLn register */ ++ if (ep->is_in == 1) { ++ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; ++ daintmsk.ep.in = 1 << ep->num; ++ } else { ++ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; ++ daintmsk.ep.out = 1 << ep->num; ++ } ++ ++ depctl.d32 = dwc_read_reg32(addr); ++ ++ depctl.b.usbactep = 0; ++ ++ if (core_if->dma_desc_enable) ++ depctl.b.epdis = 1; ++ ++ dwc_write_reg32(addr, depctl.d32); ++ ++ /* Disable the Interrupt for this EP */ ++ if (core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->deachintmsk, ++ daintmsk.d32, 0); ++ ++ if (ep->is_in == 1) { ++ dwc_write_reg32(&core_if->dev_if->dev_global_regs-> ++ diepeachintmsk[ep->num], 0); ++ } else { ++ dwc_write_reg32(&core_if->dev_if->dev_global_regs-> ++ doepeachintmsk[ep->num], 0); ++ } ++ } else { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->daintmsk, ++ daintmsk.d32, 0); ++ } ++} ++ ++/** ++ * This function initializes dma descriptor chain. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ */ ++static void init_dma_desc_chain(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ uint32_t offset; ++ uint32_t xfer_est; ++ int i; ++ ++ ep->desc_cnt = (ep->total_len / ep->maxxfer) + ++ ((ep->total_len % ep->maxxfer) ? 1 : 0); ++ if (!ep->desc_cnt) ++ ep->desc_cnt = 1; ++ ++ dma_desc = ep->desc_addr; ++ xfer_est = ep->total_len; ++ offset = 0; ++ for (i = 0; i < ep->desc_cnt; ++i) { ++ /** DMA Descriptor Setup */ ++ if (xfer_est > ep->maxxfer) { ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 0; ++ dma_desc->status.b.ioc = 0; ++ dma_desc->status.b.sp = 0; ++ dma_desc->status.b.bytes = ep->maxxfer; ++ dma_desc->buf = ep->dma_addr + offset; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ xfer_est -= ep->maxxfer; ++ offset += ep->maxxfer; ++ } else { ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ if (ep->is_in) { ++ dma_desc->status.b.sp = ++ (xfer_est % ++ ep->maxpacket) ? 1 : ((ep-> ++ sent_zlp) ? 1 : 0); ++ dma_desc->status.b.bytes = xfer_est; ++ } else { ++ dma_desc->status.b.bytes = ++ xfer_est + ((4 - (xfer_est & 0x3)) & 0x3); ++ } ++ ++ dma_desc->buf = ep->dma_addr + offset; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ } ++ dma_desc++; ++ } ++} ++ ++/** ++ * This function does the setup for a data transfer for an EP and ++ * starts the transfer. For an IN transfer, the packets will be ++ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers, ++ * the packets are unloaded from the Rx FIFO in the ISR. the ISR. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ */ ++ ++void dwc_otg_ep_start_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl; ++ deptsiz_data_t deptsiz; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s()\n", __func__); ++ DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d " ++ "xfer_buff=%p start_xfer_buff=%p, total_len = %d\n", ++ ep->num, (ep->is_in ? "IN" : "OUT"), ep->xfer_len, ++ ep->xfer_count, ep->xfer_buff, ep->start_xfer_buff, ++ ep->total_len); ++ /* IN endpoint */ ++ if (ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[ep->num]; ++ ++ gnptxsts_data_t gtxstatus; ++ ++ gtxstatus.d32 = ++ dwc_read_reg32(&core_if->core_global_regs->gnptxsts); ++ ++ if (core_if->en_multiple_tx_fifo == 0 ++ && gtxstatus.b.nptxqspcavail == 0) { ++#ifdef DEBUG ++ DWC_PRINTF("TX Queue Full (0x%0x)\n", gtxstatus.d32); ++#endif ++ return; ++ } ++ ++ depctl.d32 = dwc_read_reg32(&(in_regs->diepctl)); ++ deptsiz.d32 = dwc_read_reg32(&(in_regs->dieptsiz)); ++ ++ ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ? ++ ep->maxxfer : (ep->total_len - ep->xfer_len); ++ ++ /* Zero Length Packet? */ ++ if ((ep->xfer_len - ep->xfer_count) == 0) { ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 1; ++ } else { ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count; ++ deptsiz.b.pktcnt = ++ (ep->xfer_len - ep->xfer_count - 1 + ++ ep->maxpacket) / ep->maxpacket; ++ } ++ ++ /* Write the DMA register */ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable == 0) { ++ dwc_write_reg32(&in_regs->dieptsiz, ++ deptsiz.d32); ++ dwc_write_reg32(&(in_regs->diepdma), ++ (uint32_t) ep->dma_addr); ++ } else { ++#ifdef DWC_UTE_CFI ++ /* The descriptor chain should be already initialized by now */ ++ if (ep->buff_mode != BM_STANDARD) { ++ dwc_write_reg32(&in_regs->diepdma, ++ ep->descs_dma_addr); ++ } else { ++#endif ++ init_dma_desc_chain(core_if, ep); ++ /** DIEPDMAn Register write */ ++ dwc_write_reg32(&in_regs->diepdma, ++ ep->dma_desc_addr); ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ } ++ } else { ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ if (ep->type != DWC_OTG_EP_TYPE_ISOC) { ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, ++ * or the Tx FIFO epmty interrupt in dedicated Tx FIFO mode, ++ * the data will be written into the fifo by the ISR. ++ */ ++ if (core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&core_if-> ++ core_global_regs-> ++ gintmsk, intr_mask.d32, ++ intr_mask.d32); ++ } else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if (ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk = 1 << ep->num; ++ dwc_modify_reg32(&core_if-> ++ dev_if-> ++ dev_global_regs-> ++ dtknqr4_fifoemptymsk, ++ 0, ++ fifoemptymsk); ++ ++ } ++ } ++ } ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&in_regs->diepctl, depctl.d32); ++ ++ depctl.d32 = ++ dwc_read_reg32(&core_if->dev_if->in_ep_regs[0]->diepctl); ++ depctl.b.nextep = ep->num; ++ dwc_write_reg32(&core_if->dev_if->in_ep_regs[0]->diepctl, ++ depctl.d32); ++ ++ } else { ++ /* OUT endpoint */ ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[ep->num]; ++ ++ depctl.d32 = dwc_read_reg32(&(out_regs->doepctl)); ++ deptsiz.d32 = dwc_read_reg32(&(out_regs->doeptsiz)); ++ ++ ep->xfer_len += (ep->maxxfer < (ep->total_len - ep->xfer_len)) ? ++ ep->maxxfer : (ep->total_len - ep->xfer_len); ++ ++ /* Program the transfer size and packet count as follows: ++ * ++ * pktcnt = N ++ * xfersize = N * maxpacket ++ */ ++ if ((ep->xfer_len - ep->xfer_count) == 0) { ++ /* Zero Length Packet */ ++ deptsiz.b.xfersize = ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ } else { ++ deptsiz.b.pktcnt = ++ (ep->xfer_len - ep->xfer_count + ++ (ep->maxpacket - 1)) / ep->maxpacket; ++ ep->xfer_len = ++ deptsiz.b.pktcnt * ep->maxpacket + ep->xfer_count; ++ deptsiz.b.xfersize = ep->xfer_len - ep->xfer_count; ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "ep%d xfersize=%d pktcnt=%d\n", ++ ep->num, deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ ++ if (core_if->dma_enable) { ++ if (!core_if->dma_desc_enable) { ++ dwc_write_reg32(&out_regs->doeptsiz, ++ deptsiz.d32); ++ ++ dwc_write_reg32(&(out_regs->doepdma), ++ (uint32_t) ep->dma_addr); ++ } else { ++#ifdef DWC_UTE_CFI ++ /* The descriptor chain should be already initialized by now */ ++ if (ep->buff_mode != BM_STANDARD) { ++ dwc_write_reg32(&out_regs->doepdma, ++ ep->descs_dma_addr); ++ } else { ++#endif ++ ++ init_dma_desc_chain(core_if, ep); ++ ++ /** DOEPDMAn Register write */ ++ dwc_write_reg32(&out_regs->doepdma, ++ ep->dma_desc_addr); ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ } ++ } else { ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ } ++ ++ /* EP enable */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ ++ dwc_write_reg32(&out_regs->doepctl, depctl.d32); ++ ++ DWC_DEBUGPL(DBG_PCD, "DOEPCTL=%08x DOEPTSIZ=%08x\n", ++ dwc_read_reg32(&out_regs->doepctl), ++ dwc_read_reg32(&out_regs->doeptsiz)); ++ DWC_DEBUGPL(DBG_PCD, "DAINTMSK=%08x GINTMSK=%08x\n", ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs-> ++ daintmsk), ++ dwc_read_reg32(&core_if->core_global_regs-> ++ gintmsk)); ++ } ++} ++ ++/** ++ * This function setup a zero length transfer in Buffer DMA and ++ * Slave modes for usb requests with zero field set ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ ++ depctl_data_t depctl; ++ deptsiz_data_t deptsiz; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s()\n", __func__); ++ DWC_PRINTF("zero length transfer is called\n"); ++ ++ /* IN endpoint */ ++ if (ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[ep->num]; ++ ++ depctl.d32 = dwc_read_reg32(&(in_regs->diepctl)); ++ deptsiz.d32 = dwc_read_reg32(&(in_regs->dieptsiz)); ++ ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 1; ++ ++ /* Write the DMA register */ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable == 0) { ++ dwc_write_reg32(&in_regs->dieptsiz, ++ deptsiz.d32); ++ dwc_write_reg32(&(in_regs->diepdma), ++ (uint32_t) ep->dma_addr); ++ } ++ } else { ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, ++ * or the Tx FIFO epmty interrupt in dedicated Tx FIFO mode, ++ * the data will be written into the fifo by the ISR. ++ */ ++ if (core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&core_if->core_global_regs-> ++ gintmsk, intr_mask.d32, ++ intr_mask.d32); ++ } else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if (ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk = 1 << ep->num; ++ dwc_modify_reg32(&core_if->dev_if-> ++ dev_global_regs-> ++ dtknqr4_fifoemptymsk, ++ 0, fifoemptymsk); ++ } ++ } ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&in_regs->diepctl, depctl.d32); ++ ++ depctl.d32 = ++ dwc_read_reg32(&core_if->dev_if->in_ep_regs[0]->diepctl); ++ depctl.b.nextep = ep->num; ++ dwc_write_reg32(&core_if->dev_if->in_ep_regs[0]->diepctl, ++ depctl.d32); ++ ++ } else { ++ /* OUT endpoint */ ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[ep->num]; ++ ++ depctl.d32 = dwc_read_reg32(&(out_regs->doepctl)); ++ deptsiz.d32 = dwc_read_reg32(&(out_regs->doeptsiz)); ++ ++ /* Zero Length Packet */ ++ deptsiz.b.xfersize = ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ ++ if (core_if->dma_enable) { ++ if (!core_if->dma_desc_enable) { ++ dwc_write_reg32(&out_regs->doeptsiz, ++ deptsiz.d32); ++ ++ dwc_write_reg32(&(out_regs->doepdma), ++ (uint32_t) ep->dma_addr); ++ } ++ } else { ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ } ++ ++ /* EP enable */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ ++ dwc_write_reg32(&out_regs->doepctl, depctl.d32); ++ ++ } ++} ++ ++/** ++ * This function does the setup for a data transfer for EP0 and starts ++ * the transfer. For an IN transfer, the packets will be loaded into ++ * the appropriate Tx FIFO in the ISR. For OUT transfers, the packets are ++ * unloaded from the Rx FIFO in the ISR. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP0 data. ++ */ ++void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl; ++ deptsiz0_data_t deptsiz; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ ++ DWC_DEBUGPL(DBG_PCD, "ep%d-%s xfer_len=%d xfer_cnt=%d " ++ "xfer_buff=%p start_xfer_buff=%p \n", ++ ep->num, (ep->is_in ? "IN" : "OUT"), ep->xfer_len, ++ ep->xfer_count, ep->xfer_buff, ep->start_xfer_buff); ++ ++ ep->total_len = ep->xfer_len; ++ ++ /* IN endpoint */ ++ if (ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[0]; ++ ++ gnptxsts_data_t gtxstatus; ++ ++ gtxstatus.d32 = ++ dwc_read_reg32(&core_if->core_global_regs->gnptxsts); ++ ++ if (core_if->en_multiple_tx_fifo == 0 ++ && gtxstatus.b.nptxqspcavail == 0) { ++#ifdef DEBUG ++ deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz); ++ DWC_DEBUGPL(DBG_PCD, "DIEPCTL0=%0x\n", ++ dwc_read_reg32(&in_regs->diepctl)); ++ DWC_DEBUGPL(DBG_PCD, "DIEPTSIZ0=%0x (sz=%d, pcnt=%d)\n", ++ deptsiz.d32, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ DWC_PRINTF("TX Queue or FIFO Full (0x%0x)\n", ++ gtxstatus.d32); ++#endif ++ return; ++ } ++ ++ depctl.d32 = dwc_read_reg32(&in_regs->diepctl); ++ deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz); ++ ++ /* Zero Length Packet? */ ++ if (ep->xfer_len == 0) { ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 1; ++ } else { ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ if (ep->xfer_len > ep->maxpacket) { ++ ep->xfer_len = ep->maxpacket; ++ deptsiz.b.xfersize = ep->maxpacket; ++ } else { ++ deptsiz.b.xfersize = ep->xfer_len; ++ } ++ deptsiz.b.pktcnt = 1; ++ ++ } ++ DWC_DEBUGPL(DBG_PCDV, ++ "IN len=%d xfersize=%d pktcnt=%d [%08x]\n", ++ ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt, ++ deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable == 0) { ++ dwc_write_reg32(&in_regs->dieptsiz, ++ deptsiz.d32); ++ ++ dwc_write_reg32(&(in_regs->diepdma), ++ (uint32_t) ep->dma_addr); ++ } else { ++ dma_desc = core_if->dev_if->in_desc_addr; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.sp = ++ (ep->xfer_len == ep->maxpacket) ? 0 : 1; ++ dma_desc->status.b.bytes = ep->xfer_len; ++ dma_desc->buf = ep->dma_addr; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DIEPDMA0 Register write */ ++ dwc_write_reg32(&in_regs->diepdma, ++ core_if->dev_if-> ++ dma_in_desc_addr); ++ } ++ } else { ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&in_regs->diepctl, depctl.d32); ++ ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, the ++ * data will be written into the fifo by the ISR. ++ */ ++ if (!core_if->dma_enable) { ++ if (core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&core_if->core_global_regs-> ++ gintmsk, intr_mask.d32, ++ intr_mask.d32); ++ } else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if (ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk |= 1 << ep->num; ++ dwc_modify_reg32(&core_if->dev_if-> ++ dev_global_regs-> ++ dtknqr4_fifoemptymsk, ++ 0, fifoemptymsk); ++ } ++ } ++ } ++ } else { ++ /* OUT endpoint */ ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[0]; ++ ++ depctl.d32 = dwc_read_reg32(&out_regs->doepctl); ++ deptsiz.d32 = dwc_read_reg32(&out_regs->doeptsiz); ++ ++ /* Program the transfer size and packet count as follows: ++ * xfersize = N * (maxpacket + 4 - (maxpacket % 4)) ++ * pktcnt = N */ ++ /* Zero Length Packet */ ++ deptsiz.b.xfersize = ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ ++ DWC_DEBUGPL(DBG_PCDV, "len=%d xfersize=%d pktcnt=%d\n", ++ ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ ++ if (core_if->dma_enable) { ++ if (!core_if->dma_desc_enable) { ++ dwc_write_reg32(&out_regs->doeptsiz, ++ deptsiz.d32); ++ ++ dwc_write_reg32(&(out_regs->doepdma), ++ (uint32_t) ep->dma_addr); ++ } else { ++ dma_desc = core_if->dev_if->out_desc_addr; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.bytes = ep->maxpacket; ++ dma_desc->buf = ep->dma_addr; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DOEPDMA0 Register write */ ++ dwc_write_reg32(&out_regs->doepdma, ++ core_if->dev_if-> ++ dma_out_desc_addr); ++ } ++ } else { ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ } ++ ++ /* EP enable */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&(out_regs->doepctl), depctl.d32); ++ } ++} ++ ++/** ++ * This function continues control IN transfers started by ++ * dwc_otg_ep0_start_transfer, when the transfer does not fit in a ++ * single packet. NOTE: The DIEPCTL0/DOEPCTL0 registers only have one ++ * bit for the packet count. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP0 data. ++ */ ++void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl; ++ deptsiz0_data_t deptsiz; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ ++ if (ep->is_in == 1) { ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[0]; ++ gnptxsts_data_t tx_status = {.d32 = 0 }; ++ ++ tx_status.d32 = ++ dwc_read_reg32(&core_if->core_global_regs->gnptxsts); ++ /** @todo Should there be check for room in the Tx ++ * Status Queue. If not remove the code above this comment. */ ++ ++ depctl.d32 = dwc_read_reg32(&in_regs->diepctl); ++ deptsiz.d32 = dwc_read_reg32(&in_regs->dieptsiz); ++ ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ ++ if (core_if->dma_desc_enable == 0) { ++ deptsiz.b.xfersize = ++ (ep->total_len - ep->xfer_count) > ++ ep->maxpacket ? ep->maxpacket : (ep->total_len - ++ ep->xfer_count); ++ deptsiz.b.pktcnt = 1; ++ if (core_if->dma_enable == 0) { ++ ep->xfer_len += deptsiz.b.xfersize; ++ } else { ++ ep->xfer_len = deptsiz.b.xfersize; ++ } ++ dwc_write_reg32(&in_regs->dieptsiz, deptsiz.d32); ++ } else { ++ ep->xfer_len = ++ (ep->total_len - ep->xfer_count) > ++ ep->maxpacket ? ep->maxpacket : (ep->total_len - ++ ep->xfer_count); ++ ++ dma_desc = core_if->dev_if->in_desc_addr; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.sp = ++ (ep->xfer_len == ep->maxpacket) ? 0 : 1; ++ dma_desc->status.b.bytes = ep->xfer_len; ++ dma_desc->buf = ep->dma_addr; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DIEPDMA0 Register write */ ++ dwc_write_reg32(&in_regs->diepdma, ++ core_if->dev_if->dma_in_desc_addr); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "IN len=%d xfersize=%d pktcnt=%d [%08x]\n", ++ ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt, ++ deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (core_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH) { ++ if (core_if->dma_desc_enable == 0) ++ dwc_write_reg32(&(in_regs->diepdma), ++ (uint32_t) ep->dma_addr); ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&in_regs->diepctl, depctl.d32); ++ ++ /** ++ * Enable the Non-Periodic Tx FIFO empty interrupt, the ++ * data will be written into the fifo by the ISR. ++ */ ++ if (!core_if->dma_enable) { ++ if (core_if->en_multiple_tx_fifo == 0) { ++ /* First clear it from GINTSTS */ ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&core_if->core_global_regs-> ++ gintmsk, intr_mask.d32, ++ intr_mask.d32); ++ ++ } else { ++ /* Enable the Tx FIFO Empty Interrupt for this EP */ ++ if (ep->xfer_len > 0) { ++ uint32_t fifoemptymsk = 0; ++ fifoemptymsk |= 1 << ep->num; ++ dwc_modify_reg32(&core_if->dev_if-> ++ dev_global_regs-> ++ dtknqr4_fifoemptymsk, ++ 0, fifoemptymsk); ++ } ++ } ++ } ++ } else { ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[0]; ++ ++ depctl.d32 = dwc_read_reg32(&out_regs->doepctl); ++ deptsiz.d32 = dwc_read_reg32(&out_regs->doeptsiz); ++ ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.xfersize = ep->maxpacket; ++ deptsiz.b.pktcnt = 1; ++ ++ if (core_if->dma_desc_enable == 0) { ++ dwc_write_reg32(&out_regs->doeptsiz, deptsiz.d32); ++ } else { ++ dma_desc = core_if->dev_if->out_desc_addr; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.bytes = ep->maxpacket; ++ dma_desc->buf = ep->dma_addr; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DOEPDMA0 Register write */ ++ dwc_write_reg32(&out_regs->doepdma, ++ core_if->dev_if->dma_out_desc_addr); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "IN len=%d xfersize=%d pktcnt=%d [%08x]\n", ++ ep->xfer_len, deptsiz.b.xfersize, deptsiz.b.pktcnt, ++ deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (core_if->hwcfg2.b.architecture == DWC_INT_DMA_ARCH) { ++ if (core_if->dma_desc_enable == 0) ++ dwc_write_reg32(&(out_regs->doepdma), ++ (uint32_t) ep->dma_addr); ++ } ++ ++ /* EP enable, IN data in FIFO */ ++ depctl.b.cnak = 1; ++ depctl.b.epena = 1; ++ dwc_write_reg32(&out_regs->doepctl, depctl.d32); ++ ++ } ++} ++ ++#ifdef DEBUG ++void dump_msg(const u8 * buf, unsigned int length) ++{ ++ unsigned int start, num, i; ++ char line[52], *p; ++ ++ if (length >= 512) ++ return; ++ start = 0; ++ while (length > 0) { ++ num = length < 16u ? length : 16u; ++ p = line; ++ for (i = 0; i < num; ++i) { ++ if (i == 8) ++ *p++ = ' '; ++ DWC_SPRINTF(p, " %02x", buf[i]); ++ p += 3; ++ } ++ *p = 0; ++ DWC_PRINTF("%6x: %s\n", start, line); ++ buf += num; ++ start += num; ++ length -= num; ++ } ++} ++#else ++static inline void dump_msg(const u8 * buf, unsigned int length) ++{ ++} ++#endif ++ ++/** ++ * This function writes a packet into the Tx FIFO associated with the ++ * EP. For non-periodic EPs the non-periodic Tx FIFO is written. For ++ * periodic EPs the periodic Tx FIFO associated with the EP is written ++ * with all packets for the next micro-frame. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to write packet for. ++ * @param dma Indicates if DMA is being used. ++ */ ++void dwc_otg_ep_write_packet(dwc_otg_core_if_t * core_if, dwc_ep_t * ep, ++ int dma) ++{ ++ /** ++ * The buffer is padded to DWORD on a per packet basis in ++ * slave/dma mode if the MPS is not DWORD aligned. The last ++ * packet, if short, is also padded to a multiple of DWORD. ++ * ++ * ep->xfer_buff always starts DWORD aligned in memory and is a ++ * multiple of DWORD in length ++ * ++ * ep->xfer_len can be any number of bytes ++ * ++ * ep->xfer_count is a multiple of ep->maxpacket until the last ++ * packet ++ * ++ * FIFO access is DWORD */ ++ ++ uint32_t i; ++ uint32_t byte_count; ++ uint32_t dword_count; ++ uint32_t *fifo; ++ uint32_t *data_buff = (uint32_t *) ep->xfer_buff; ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p)\n", __func__, core_if, ++ ep); ++ if (ep->xfer_count >= ep->xfer_len) { ++ DWC_WARN("%s() No data for EP%d!!!\n", __func__, ep->num); ++ return; ++ } ++ ++ /* Find the byte length of the packet either short packet or MPS */ ++ if ((ep->xfer_len - ep->xfer_count) < ep->maxpacket) { ++ byte_count = ep->xfer_len - ep->xfer_count; ++ } else { ++ byte_count = ep->maxpacket; ++ } ++ ++ /* Find the DWORD length, padded by extra bytes as neccessary if MPS ++ * is not a multiple of DWORD */ ++ dword_count = (byte_count + 3) / 4; ++ ++#ifdef VERBOSE ++ dump_msg(ep->xfer_buff, byte_count); ++#endif ++ ++ /**@todo NGS Where are the Periodic Tx FIFO addresses ++ * intialized? What should this be? */ ++ ++ fifo = core_if->data_fifo[ep->num]; ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "fifo=%p buff=%p *p=%08x bc=%d\n", ++ fifo, data_buff, *data_buff, byte_count); ++ ++ if (!dma) { ++ for (i = 0; i < dword_count; i++, data_buff++) { ++ dwc_write_reg32(fifo, *data_buff); ++ } ++ } ++ ++ ep->xfer_count += byte_count; ++ ep->xfer_buff += byte_count; ++ ep->dma_addr += byte_count; ++} ++ ++/** ++ * Set the EP STALL. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to set the stall on. ++ */ ++void dwc_otg_ep_set_stall(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl; ++ volatile uint32_t *depctl_addr; ++ ++ DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, ep->num, ++ (ep->is_in ? "IN" : "OUT")); ++ ++ if (ep->is_in == 1) { ++ depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl); ++ depctl.d32 = dwc_read_reg32(depctl_addr); ++ ++ /* set the disable and stall bits */ ++ if (depctl.b.epena) { ++ depctl.b.epdis = 1; ++ } ++ depctl.b.stall = 1; ++ dwc_write_reg32(depctl_addr, depctl.d32); ++ } else { ++ depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl); ++ depctl.d32 = dwc_read_reg32(depctl_addr); ++ ++ /* set the stall bit */ ++ depctl.b.stall = 1; ++ dwc_write_reg32(depctl_addr, depctl.d32); ++ } ++ ++ DWC_DEBUGPL(DBG_PCD, "DEPCTL=%0x\n", dwc_read_reg32(depctl_addr)); ++ ++ return; ++} ++ ++/** ++ * Clear the EP STALL. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to clear stall from. ++ */ ++void dwc_otg_ep_clear_stall(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl; ++ volatile uint32_t *depctl_addr; ++ ++ DWC_DEBUGPL(DBG_PCD, "%s ep%d-%s\n", __func__, ep->num, ++ (ep->is_in ? "IN" : "OUT")); ++ ++ if (ep->is_in == 1) { ++ depctl_addr = &(core_if->dev_if->in_ep_regs[ep->num]->diepctl); ++ } else { ++ depctl_addr = &(core_if->dev_if->out_ep_regs[ep->num]->doepctl); ++ } ++ ++ depctl.d32 = dwc_read_reg32(depctl_addr); ++ ++ /* clear the stall bits */ ++ depctl.b.stall = 0; ++ ++ /* ++ * USB Spec 9.4.5: For endpoints using data toggle, regardless ++ * of whether an endpoint has the Halt feature set, a ++ * ClearFeature(ENDPOINT_HALT) request always results in the ++ * data toggle being reinitialized to DATA0. ++ */ ++ if (ep->type == DWC_OTG_EP_TYPE_INTR || ++ ep->type == DWC_OTG_EP_TYPE_BULK) { ++ depctl.b.setd0pid = 1; /* DATA0 */ ++ } ++ ++ dwc_write_reg32(depctl_addr, depctl.d32); ++ DWC_DEBUGPL(DBG_PCD, "DEPCTL=%0x\n", dwc_read_reg32(depctl_addr)); ++ return; ++} ++ ++/** ++ * This function reads a packet from the Rx FIFO into the destination ++ * buffer. To read SETUP data use dwc_otg_read_setup_packet. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dest Destination buffer for the packet. ++ * @param bytes Number of bytes to copy to the destination. ++ */ ++void dwc_otg_read_packet(dwc_otg_core_if_t * core_if, ++ uint8_t * dest, uint16_t bytes) ++{ ++ int i; ++ int word_count = (bytes + 3) / 4; ++ ++ volatile uint32_t *fifo = core_if->data_fifo[0]; ++ uint32_t *data_buff = (uint32_t *) dest; ++ ++ /** ++ * @todo Account for the case where _dest is not dword aligned. This ++ * requires reading data from the FIFO into a uint32_t temp buffer, ++ * then moving it into the data buffer. ++ */ ++ ++ DWC_DEBUGPL((DBG_PCDV | DBG_CILV), "%s(%p,%p,%d)\n", __func__, ++ core_if, dest, bytes); ++ ++ for (i = 0; i < word_count; i++, data_buff++) { ++ *data_buff = dwc_read_reg32(fifo); ++ } ++ ++ return; ++} ++ ++/** ++ * This functions reads the device registers and prints them ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_dump_dev_registers(dwc_otg_core_if_t * core_if) ++{ ++ int i; ++ volatile uint32_t *addr; ++ ++ DWC_PRINTF("Device Global Registers\n"); ++ addr = &core_if->dev_if->dev_global_regs->dcfg; ++ DWC_PRINTF("DCFG @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->dctl; ++ DWC_PRINTF("DCTL @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->dsts; ++ DWC_PRINTF("DSTS @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->diepmsk; ++ DWC_PRINTF("DIEPMSK @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->doepmsk; ++ DWC_PRINTF("DOEPMSK @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->daint; ++ DWC_PRINTF("DAINT @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->daintmsk; ++ DWC_PRINTF("DAINTMSK @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->dtknqr1; ++ DWC_PRINTF("DTKNQR1 @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ if (core_if->hwcfg2.b.dev_token_q_depth > 6) { ++ addr = &core_if->dev_if->dev_global_regs->dtknqr2; ++ DWC_PRINTF("DTKNQR2 @0x%08X : 0x%08X\n", ++ (uint32_t) addr, dwc_read_reg32(addr)); ++ } ++ ++ addr = &core_if->dev_if->dev_global_regs->dvbusdis; ++ DWC_PRINTF("DVBUSID @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ ++ addr = &core_if->dev_if->dev_global_regs->dvbuspulse; ++ DWC_PRINTF("DVBUSPULSE @0x%08X : 0x%08X\n", ++ (uint32_t) addr, dwc_read_reg32(addr)); ++ ++ addr = &core_if->dev_if->dev_global_regs->dtknqr3_dthrctl; ++ DWC_PRINTF("DTKNQR3_DTHRCTL @0x%08X : 0x%08X\n", ++ (uint32_t) addr, dwc_read_reg32(addr)); ++ ++ if (core_if->hwcfg2.b.dev_token_q_depth > 22) { ++ addr = &core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk; ++ DWC_PRINTF("DTKNQR4 @0x%08X : 0x%08X\n", ++ (uint32_t) addr, dwc_read_reg32(addr)); ++ } ++ ++ addr = &core_if->dev_if->dev_global_regs->dtknqr4_fifoemptymsk; ++ DWC_PRINTF("FIFOEMPMSK @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ ++ addr = &core_if->dev_if->dev_global_regs->deachint; ++ DWC_PRINTF("DEACHINT @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->dev_global_regs->deachintmsk; ++ DWC_PRINTF("DEACHINTMSK @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ addr = &core_if->dev_if->dev_global_regs->diepeachintmsk[i]; ++ DWC_PRINTF("DIEPEACHINTMSK[%d] @0x%08X : 0x%08X\n", i, ++ (uint32_t) addr, dwc_read_reg32(addr)); ++ } ++ ++ for (i = 0; i <= core_if->dev_if->num_out_eps; i++) { ++ addr = &core_if->dev_if->dev_global_regs->doepeachintmsk[i]; ++ DWC_PRINTF("DOEPEACHINTMSK[%d] @0x%08X : 0x%08X\n", i, ++ (uint32_t) addr, dwc_read_reg32(addr)); ++ } ++ ++ for (i = 0; i <= core_if->dev_if->num_in_eps; i++) { ++ DWC_PRINTF("Device IN EP %d Registers\n", i); ++ addr = &core_if->dev_if->in_ep_regs[i]->diepctl; ++ DWC_PRINTF("DIEPCTL @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->in_ep_regs[i]->diepint; ++ DWC_PRINTF("DIEPINT @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->in_ep_regs[i]->dieptsiz; ++ DWC_PRINTF("DIETSIZ @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->in_ep_regs[i]->diepdma; ++ DWC_PRINTF("DIEPDMA @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->in_ep_regs[i]->dtxfsts; ++ DWC_PRINTF("DTXFSTS @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->in_ep_regs[i]->diepdmab; ++ DWC_PRINTF("DIEPDMAB @0x%08X : 0x%08X\n", (uint32_t) addr, ++ 0 /*dwc_read_reg32(addr) */ ); ++ } ++ ++ for (i = 0; i <= core_if->dev_if->num_out_eps; i++) { ++ DWC_PRINTF("Device OUT EP %d Registers\n", i); ++ addr = &core_if->dev_if->out_ep_regs[i]->doepctl; ++ DWC_PRINTF("DOEPCTL @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->out_ep_regs[i]->doepfn; ++ DWC_PRINTF("DOEPFN @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->out_ep_regs[i]->doepint; ++ DWC_PRINTF("DOEPINT @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->out_ep_regs[i]->doeptsiz; ++ DWC_PRINTF("DOETSIZ @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->dev_if->out_ep_regs[i]->doepdma; ++ DWC_PRINTF("DOEPDMA @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ if (core_if->dma_enable) { /* Don't access this register in SLAVE mode */ ++ addr = &core_if->dev_if->out_ep_regs[i]->doepdmab; ++ DWC_PRINTF("DOEPDMAB @0x%08X : 0x%08X\n", ++ (uint32_t) addr, dwc_read_reg32(addr)); ++ } ++ ++ } ++} ++ ++/** ++ * This functions reads the SPRAM and prints its content ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_dump_spram(dwc_otg_core_if_t * core_if) ++{ ++ volatile uint8_t *addr, *start_addr, *end_addr; ++ ++ DWC_PRINTF("SPRAM Data:\n"); ++ start_addr = (void *)core_if->core_global_regs; ++ DWC_PRINTF("Base Address: 0x%8X\n", (uint32_t) start_addr); ++ start_addr += 0x00028000; ++ end_addr = (void *)core_if->core_global_regs; ++ end_addr += 0x000280e0; ++ ++ for (addr = start_addr; addr < end_addr; addr += 16) { ++ DWC_PRINTF ++ ("0x%8X:\t%2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X %2X\n", ++ (uint32_t) addr, addr[0], addr[1], addr[2], addr[3], ++ addr[4], addr[5], addr[6], addr[7], addr[8], addr[9], ++ addr[10], addr[11], addr[12], addr[13], addr[14], addr[15] ++ ); ++ } ++ ++ return; ++} ++ ++/** ++ * This function reads the host registers and prints them ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_dump_host_registers(dwc_otg_core_if_t * core_if) ++{ ++ int i; ++ volatile uint32_t *addr; ++ ++ DWC_PRINTF("Host Global Registers\n"); ++ addr = &core_if->host_if->host_global_regs->hcfg; ++ DWC_PRINTF("HCFG @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->host_if->host_global_regs->hfir; ++ DWC_PRINTF("HFIR @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->host_if->host_global_regs->hfnum; ++ DWC_PRINTF("HFNUM @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->host_if->host_global_regs->hptxsts; ++ DWC_PRINTF("HPTXSTS @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->host_if->host_global_regs->haint; ++ DWC_PRINTF("HAINT @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->host_if->host_global_regs->haintmsk; ++ DWC_PRINTF("HAINTMSK @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ if (core_if->dma_desc_enable) { ++ addr = &core_if->host_if->host_global_regs->hflbaddr; ++ DWC_PRINTF("HFLBADDR @0x%08X : 0x%08X\n",(uint32_t) addr, ++ dwc_read_reg32(addr)); ++ } ++ ++ addr = core_if->host_if->hprt0; ++ DWC_PRINTF("HPRT0 @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ ++ for (i = 0; i < core_if->core_params->host_channels; i++) { ++ DWC_PRINTF("Host Channel %d Specific Registers\n", i); ++ addr = &core_if->host_if->hc_regs[i]->hcchar; ++ DWC_PRINTF("HCCHAR @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->host_if->hc_regs[i]->hcsplt; ++ DWC_PRINTF("HCSPLT @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->host_if->hc_regs[i]->hcint; ++ DWC_PRINTF("HCINT @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->host_if->hc_regs[i]->hcintmsk; ++ DWC_PRINTF("HCINTMSK @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->host_if->hc_regs[i]->hctsiz; ++ DWC_PRINTF("HCTSIZ @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->host_if->hc_regs[i]->hcdma; ++ DWC_PRINTF("HCDMA @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ if (core_if->dma_desc_enable) { ++ addr=&core_if->host_if->hc_regs[i]->hcdmab; ++ DWC_PRINTF("HCDMAB @0x%08X : 0x%08X\n",(uint32_t) addr, dwc_read_reg32(addr)); ++ } ++ ++ } ++ return; ++} ++ ++/** ++ * This function reads the core global registers and prints them ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_dump_global_registers(dwc_otg_core_if_t * core_if) ++{ ++ int i; ++ volatile uint32_t *addr; ++ ++ DWC_PRINTF("Core Global Registers\n"); ++ addr = &core_if->core_global_regs->gotgctl; ++ DWC_PRINTF("GOTGCTL @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->gotgint; ++ DWC_PRINTF("GOTGINT @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->gahbcfg; ++ DWC_PRINTF("GAHBCFG @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->gusbcfg; ++ DWC_PRINTF("GUSBCFG @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->grstctl; ++ DWC_PRINTF("GRSTCTL @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->gintsts; ++ DWC_PRINTF("GINTSTS @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->gintmsk; ++ DWC_PRINTF("GINTMSK @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->grxstsr; ++ DWC_PRINTF("GRXSTSR @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->grxfsiz; ++ DWC_PRINTF("GRXFSIZ @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->gnptxfsiz; ++ DWC_PRINTF("GNPTXFSIZ @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->gnptxsts; ++ DWC_PRINTF("GNPTXSTS @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->gi2cctl; ++ DWC_PRINTF("GI2CCTL @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->gpvndctl; ++ DWC_PRINTF("GPVNDCTL @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->ggpio; ++ DWC_PRINTF("GGPIO @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->guid; ++ DWC_PRINTF("GUID @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->gsnpsid; ++ DWC_PRINTF("GSNPSID @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->ghwcfg1; ++ DWC_PRINTF("GHWCFG1 @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->ghwcfg2; ++ DWC_PRINTF("GHWCFG2 @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->ghwcfg3; ++ DWC_PRINTF("GHWCFG3 @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->ghwcfg4; ++ DWC_PRINTF("GHWCFG4 @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->glpmcfg; ++ DWC_PRINTF("GLPMCFG @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ addr = &core_if->core_global_regs->hptxfsiz; ++ DWC_PRINTF("HPTXFSIZ @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; i++) { ++ addr = &core_if->core_global_regs->dptxfsiz_dieptxf[i]; ++ DWC_PRINTF("DPTXFSIZ[%d] @0x%08X : 0x%08X\n", i, ++ (uint32_t) addr, dwc_read_reg32(addr)); ++ } ++ addr = core_if->pcgcctl; ++ DWC_PRINTF("PCGCCTL @0x%08X : 0x%08X\n", (uint32_t) addr, ++ dwc_read_reg32(addr)); ++} ++ ++/** ++ * Flush a Tx FIFO. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param num Tx FIFO to flush. ++ */ ++void dwc_otg_flush_tx_fifo(dwc_otg_core_if_t * core_if, const int num) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ volatile grstctl_t greset = {.d32 = 0 }; ++ int count = 0; ++ ++ DWC_DEBUGPL((DBG_CIL | DBG_PCDV), "Flush Tx FIFO %d\n", num); ++ ++ greset.b.txfflsh = 1; ++ greset.b.txfnum = num; ++ dwc_write_reg32(&global_regs->grstctl, greset.d32); ++ ++ do { ++ greset.d32 = dwc_read_reg32(&global_regs->grstctl); ++ if (++count > 10000) { ++ DWC_WARN("%s() HANG! GRSTCTL=%0x GNPTXSTS=0x%08x\n", ++ __func__, greset.d32, ++ dwc_read_reg32(&global_regs->gnptxsts)); ++ break; ++ } ++ dwc_udelay(1); ++ } while (greset.b.txfflsh == 1); ++ ++ /* Wait for 3 PHY Clocks */ ++ dwc_udelay(1); ++} ++ ++/** ++ * Flush Rx FIFO. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++void dwc_otg_flush_rx_fifo(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ volatile grstctl_t greset = {.d32 = 0 }; ++ int count = 0; ++ ++ DWC_DEBUGPL((DBG_CIL | DBG_PCDV), "%s\n", __func__); ++ /* ++ * ++ */ ++ greset.b.rxfflsh = 1; ++ dwc_write_reg32(&global_regs->grstctl, greset.d32); ++ ++ do { ++ greset.d32 = dwc_read_reg32(&global_regs->grstctl); ++ if (++count > 10000) { ++ DWC_WARN("%s() HANG! GRSTCTL=%0x\n", __func__, ++ greset.d32); ++ break; ++ } ++ dwc_udelay(1); ++ } while (greset.b.rxfflsh == 1); ++ ++ /* Wait for 3 PHY Clocks */ ++ dwc_udelay(1); ++} ++ ++/** ++ * Do core a soft reset of the core. Be careful with this because it ++ * resets all the internal state machines of the core. ++ */ ++void dwc_otg_core_reset(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ volatile grstctl_t greset = {.d32 = 0 }; ++ int count = 0; ++ ++ DWC_DEBUGPL(DBG_CILV, "%s\n", __func__); ++ /* Wait for AHB master IDLE state. */ ++ do { ++ dwc_udelay(10); ++ greset.d32 = dwc_read_reg32(&global_regs->grstctl); ++ if (++count > 100000) { ++ DWC_WARN("%s() HANG! AHB Idle GRSTCTL=%0x\n", __func__, ++ greset.d32); ++ return; ++ } ++ } ++ while (greset.b.ahbidle == 0); ++ ++ /* Core Soft Reset */ ++ count = 0; ++ greset.b.csftrst = 1; ++ dwc_write_reg32(&global_regs->grstctl, greset.d32); ++ do { ++ greset.d32 = dwc_read_reg32(&global_regs->grstctl); ++ if (++count > 10000) { ++ DWC_WARN("%s() HANG! Soft Reset GRSTCTL=%0x\n", ++ __func__, greset.d32); ++ break; ++ } ++ dwc_udelay(1); ++ } ++ while (greset.b.csftrst == 1); ++ ++ /* Wait for 3 PHY Clocks */ ++ dwc_mdelay(100); ++} ++ ++uint8_t dwc_otg_is_device_mode(dwc_otg_core_if_t * _core_if) ++{ ++ return (dwc_otg_mode(_core_if) != DWC_HOST_MODE); ++} ++ ++uint8_t dwc_otg_is_host_mode(dwc_otg_core_if_t * _core_if) ++{ ++ return (dwc_otg_mode(_core_if) == DWC_HOST_MODE); ++} ++ ++/** ++ * Register HCD callbacks. The callbacks are used to start and stop ++ * the HCD for interrupt processing. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param cb the HCD callback structure. ++ * @param p pointer to be passed to callback function (usb_hcd*). ++ */ ++void dwc_otg_cil_register_hcd_callbacks(dwc_otg_core_if_t * core_if, ++ dwc_otg_cil_callbacks_t * cb, void *p) ++{ ++ core_if->hcd_cb = cb; ++ cb->p = p; ++} ++ ++/** ++ * Register PCD callbacks. The callbacks are used to start and stop ++ * the PCD for interrupt processing. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param cb the PCD callback structure. ++ * @param p pointer to be passed to callback function (pcd*). ++ */ ++void dwc_otg_cil_register_pcd_callbacks(dwc_otg_core_if_t * core_if, ++ dwc_otg_cil_callbacks_t * cb, void *p) ++{ ++ core_if->pcd_cb = cb; ++ cb->p = p; ++} ++ ++#ifdef DWC_EN_ISOC ++ ++/** ++ * This function writes isoc data per 1 (micro)frame into tx fifo ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++void write_isoc_frame_data(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ dwc_otg_dev_in_ep_regs_t *ep_regs; ++ dtxfsts_data_t txstatus = {.d32 = 0 }; ++ uint32_t len = 0; ++ uint32_t dwords; ++ ++ ep->xfer_len = ep->data_per_frame; ++ ep->xfer_count = 0; ++ ++ ep_regs = core_if->dev_if->in_ep_regs[ep->num]; ++ ++ len = ep->xfer_len - ep->xfer_count; ++ ++ if (len > ep->maxpacket) { ++ len = ep->maxpacket; ++ } ++ ++ dwords = (len + 3) / 4; ++ ++ /* While there is space in the queue and space in the FIFO and ++ * More data to tranfer, Write packets to the Tx FIFO */ ++ txstatus.d32 = ++ dwc_read_reg32(&core_if->dev_if->in_ep_regs[ep->num]->dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", ep->num, txstatus.d32); ++ ++ while (txstatus.b.txfspcavail > dwords && ++ ep->xfer_count < ep->xfer_len && ep->xfer_len != 0) { ++ /* Write the FIFO */ ++ dwc_otg_ep_write_packet(core_if, ep, 0); ++ ++ len = ep->xfer_len - ep->xfer_count; ++ if (len > ep->maxpacket) { ++ len = ep->maxpacket; ++ } ++ ++ dwords = (len + 3) / 4; ++ txstatus.d32 = ++ dwc_read_reg32(&core_if->dev_if->in_ep_regs[ep->num]-> ++ dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", ep->num, ++ txstatus.d32); ++ } ++} ++ ++/** ++ * This function initializes a descriptor chain for Isochronous transfer ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++void dwc_otg_iso_ep_start_frm_transfer(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * ep) ++{ ++ deptsiz_data_t deptsiz = {.d32 = 0 }; ++ depctl_data_t depctl = {.d32 = 0 }; ++ dsts_data_t dsts = {.d32 = 0 }; ++ volatile uint32_t *addr; ++ ++ if (ep->is_in) { ++ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; ++ } else { ++ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; ++ } ++ ++ ep->xfer_len = ep->data_per_frame; ++ ep->xfer_count = 0; ++ ep->xfer_buff = ep->cur_pkt_addr; ++ ep->dma_addr = ep->cur_pkt_dma_addr; ++ ++ if (ep->is_in) { ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.xfersize = ep->xfer_len; ++ deptsiz.b.pktcnt = ++ (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket; ++ deptsiz.b.mc = deptsiz.b.pktcnt; ++ dwc_write_reg32(&core_if->dev_if->in_ep_regs[ep->num]->dieptsiz, ++ deptsiz.d32); ++ ++ /* Write the DMA register */ ++ if (core_if->dma_enable) { ++ dwc_write_reg32(& ++ (core_if->dev_if->in_ep_regs[ep->num]-> ++ diepdma), (uint32_t) ep->dma_addr); ++ } ++ } else { ++ deptsiz.b.pktcnt = ++ (ep->xfer_len + (ep->maxpacket - 1)) / ep->maxpacket; ++ deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket; ++ ++ dwc_write_reg32(&core_if->dev_if->out_ep_regs[ep->num]-> ++ doeptsiz, deptsiz.d32); ++ ++ if (core_if->dma_enable) { ++ dwc_write_reg32(& ++ (core_if->dev_if->out_ep_regs[ep->num]-> ++ doepdma), (uint32_t) ep->dma_addr); ++ } ++ } ++ ++ /** Enable endpoint, clear nak */ ++ ++ depctl.d32 = 0; ++ if (ep->bInterval == 1) { ++ dsts.d32 = ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ ep->next_frame = dsts.b.soffn + ep->bInterval; ++ ++ if (ep->next_frame & 0x1) { ++ depctl.b.setd1pid = 1; ++ } else { ++ depctl.b.setd0pid = 1; ++ } ++ } else { ++ ep->next_frame += ep->bInterval; ++ ++ if (ep->next_frame & 0x1) { ++ depctl.b.setd1pid = 1; ++ } else { ++ depctl.b.setd0pid = 1; ++ } ++ } ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ ++ dwc_modify_reg32(addr, 0, depctl.d32); ++ depctl.d32 = dwc_read_reg32(addr); ++ ++ if (ep->is_in && core_if->dma_enable == 0) { ++ write_isoc_frame_data(core_if, ep); ++ } ++ ++} ++#endif /* DWC_EN_ISOC */ ++ ++static void dwc_otg_set_uninitialized(int32_t * p, int size) ++{ ++ int i; ++ for (i = 0; i < size; i++) { ++ p[i] = -1; ++ } ++} ++ ++static int dwc_otg_param_initialized(int32_t val) ++{ ++ return val != -1; ++} ++ ++static int dwc_otg_setup_params(dwc_otg_core_if_t * core_if) ++{ ++ int i; ++ core_if->core_params = DWC_ALLOC(sizeof(*core_if->core_params)); ++ if (!core_if->core_params) { ++ return -DWC_E_NO_MEMORY; ++ } ++ dwc_otg_set_uninitialized((int32_t *) core_if->core_params, ++ sizeof(*core_if->core_params) / ++ sizeof(int32_t)); ++ DWC_PRINTF("Setting default values for core params\n"); ++ dwc_otg_set_param_otg_cap(core_if, dwc_param_otg_cap_default); ++ dwc_otg_set_param_dma_enable(core_if, dwc_param_dma_enable_default); ++ dwc_otg_set_param_dma_desc_enable(core_if, ++ dwc_param_dma_desc_enable_default); ++ dwc_otg_set_param_opt(core_if, dwc_param_opt_default); ++ dwc_otg_set_param_dma_burst_size(core_if, ++ dwc_param_dma_burst_size_default); ++ dwc_otg_set_param_host_support_fs_ls_low_power(core_if, ++ dwc_param_host_support_fs_ls_low_power_default); ++ dwc_otg_set_param_enable_dynamic_fifo(core_if, ++ dwc_param_enable_dynamic_fifo_default); ++ dwc_otg_set_param_data_fifo_size(core_if, ++ dwc_param_data_fifo_size_default); ++ dwc_otg_set_param_dev_rx_fifo_size(core_if, ++ dwc_param_dev_rx_fifo_size_default); ++ dwc_otg_set_param_dev_nperio_tx_fifo_size(core_if, ++ dwc_param_dev_nperio_tx_fifo_size_default); ++ dwc_otg_set_param_host_rx_fifo_size(core_if, ++ dwc_param_host_rx_fifo_size_default); ++ dwc_otg_set_param_host_nperio_tx_fifo_size(core_if, ++ dwc_param_host_nperio_tx_fifo_size_default); ++ dwc_otg_set_param_host_perio_tx_fifo_size(core_if, ++ dwc_param_host_perio_tx_fifo_size_default); ++ dwc_otg_set_param_max_transfer_size(core_if, ++ dwc_param_max_transfer_size_default); ++ dwc_otg_set_param_max_packet_count(core_if, ++ dwc_param_max_packet_count_default); ++ dwc_otg_set_param_host_channels(core_if, ++ dwc_param_host_channels_default); ++ dwc_otg_set_param_dev_endpoints(core_if, ++ dwc_param_dev_endpoints_default); ++ dwc_otg_set_param_phy_type(core_if, dwc_param_phy_type_default); ++ dwc_otg_set_param_speed(core_if, dwc_param_speed_default); ++ dwc_otg_set_param_host_ls_low_power_phy_clk(core_if, ++ dwc_param_host_ls_low_power_phy_clk_default); ++ dwc_otg_set_param_phy_ulpi_ddr(core_if, dwc_param_phy_ulpi_ddr_default); ++ dwc_otg_set_param_phy_ulpi_ext_vbus(core_if, ++ dwc_param_phy_ulpi_ext_vbus_default); ++ dwc_otg_set_param_phy_utmi_width(core_if, ++ dwc_param_phy_utmi_width_default); ++ dwc_otg_set_param_ts_dline(core_if, dwc_param_ts_dline_default); ++ dwc_otg_set_param_i2c_enable(core_if, dwc_param_i2c_enable_default); ++ dwc_otg_set_param_ulpi_fs_ls(core_if, dwc_param_ulpi_fs_ls_default); ++ dwc_otg_set_param_en_multiple_tx_fifo(core_if, ++ dwc_param_en_multiple_tx_fifo_default); ++ for (i = 0; i < 15; i++) { ++ dwc_otg_set_param_dev_perio_tx_fifo_size(core_if, ++ dwc_param_dev_perio_tx_fifo_size_default, ++ i); ++ } ++ ++ for (i = 0; i < 15; i++) { ++ dwc_otg_set_param_dev_tx_fifo_size(core_if, ++ dwc_param_dev_tx_fifo_size_default, ++ i); ++ } ++ dwc_otg_set_param_thr_ctl(core_if, dwc_param_thr_ctl_default); ++ dwc_otg_set_param_mpi_enable(core_if, dwc_param_mpi_enable_default); ++ dwc_otg_set_param_pti_enable(core_if, dwc_param_pti_enable_default); ++ dwc_otg_set_param_lpm_enable(core_if, dwc_param_lpm_enable_default); ++ dwc_otg_set_param_ic_usb_cap(core_if, dwc_param_ic_usb_cap_default); ++ dwc_otg_set_param_tx_thr_length(core_if, ++ dwc_param_tx_thr_length_default); ++ dwc_otg_set_param_rx_thr_length(core_if, ++ dwc_param_rx_thr_length_default); ++ dwc_otg_set_param_ahb_thr_ratio(core_if, dwc_param_ahb_thr_ratio_default); ++ DWC_PRINTF("Finished setting default values for core params\n"); ++ return 0; ++} ++ ++uint8_t dwc_otg_is_dma_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->dma_enable; ++} ++ ++/* Checks if the parameter is outside of its valid range of values */ ++#define DWC_OTG_PARAM_TEST(_param_, _low_, _high_) \ ++ (((_param_) < (_low_)) || \ ++ ((_param_) > (_high_))) ++ ++/* Parameter access functions */ ++int dwc_otg_set_param_otg_cap(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int valid; ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 2)) { ++ DWC_WARN("Wrong value for otg_cap parameter\n"); ++ DWC_WARN("otg_cap parameter must be 0,1 or 2\n"); ++ retval = -DWC_E_INVALID; ++ goto out; ++ } ++ ++ valid = 1; ++ switch (val) { ++ case DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE: ++ if (core_if->hwcfg2.b.op_mode != ++ DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) ++ valid = 0; ++ break; ++ case DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE: ++ if ((core_if->hwcfg2.b.op_mode != ++ DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) ++ && (core_if->hwcfg2.b.op_mode != ++ DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) ++ && (core_if->hwcfg2.b.op_mode != ++ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ++ && (core_if->hwcfg2.b.op_mode != ++ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) { ++ valid = 0; ++ } ++ break; ++ case DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE: ++ /* always valid */ ++ break; ++ } ++ if (!valid) { ++ if (dwc_otg_param_initialized(core_if->core_params->otg_cap)) { ++ DWC_ERROR ++ ("%d invalid for otg_cap paremter. Check HW configuration.\n", ++ val); ++ } ++ val = ++ (((core_if->hwcfg2.b.op_mode == ++ DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG) ++ || (core_if->hwcfg2.b.op_mode == ++ DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG) ++ || (core_if->hwcfg2.b.op_mode == ++ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE) ++ || (core_if->hwcfg2.b.op_mode == ++ DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST)) ? ++ DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE : ++ DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->otg_cap = val; ++ out: ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_otg_cap(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->otg_cap; ++} ++ ++int dwc_otg_set_param_opt(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for opt parameter\n"); ++ return -DWC_E_INVALID; ++ } ++ core_if->core_params->opt = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_opt(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->opt; ++} ++ ++int dwc_otg_set_param_dma_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for dma enable\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val == 1) && (core_if->hwcfg2.b.architecture == 0)) { ++ if (dwc_otg_param_initialized(core_if->core_params->dma_enable)) { ++ DWC_ERROR ++ ("%d invalid for dma_enable paremter. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->dma_enable = val; ++ if (val == 0) { ++ dwc_otg_set_param_dma_desc_enable(core_if, 0); ++ } ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dma_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->dma_enable; ++} ++ ++int dwc_otg_set_param_dma_desc_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for dma_enable\n"); ++ DWC_WARN("dma_desc_enable must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val == 1) ++ && ((dwc_otg_get_param_dma_enable(core_if) == 0) ++ || (core_if->hwcfg4.b.desc_dma == 0))) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->dma_desc_enable)) { ++ DWC_ERROR ++ ("%d invalid for dma_desc_enable paremter. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++ core_if->core_params->dma_desc_enable = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dma_desc_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->dma_desc_enable; ++} ++ ++int dwc_otg_set_param_host_support_fs_ls_low_power(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for host_support_fs_low_power\n"); ++ DWC_WARN("host_support_fs_low_power must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ core_if->core_params->host_support_fs_ls_low_power = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_host_support_fs_ls_low_power(dwc_otg_core_if_t * ++ core_if) ++{ ++ return core_if->core_params->host_support_fs_ls_low_power; ++} ++ ++int dwc_otg_set_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for enable_dynamic_fifo\n"); ++ DWC_WARN("enable_dynamic_fifo must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val == 1) && (core_if->hwcfg2.b.dynamic_fifo == 0)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->enable_dynamic_fifo)) { ++ DWC_ERROR ++ ("%d invalid for enable_dynamic_fifo paremter. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++ core_if->core_params->enable_dynamic_fifo = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->enable_dynamic_fifo; ++} ++ ++int dwc_otg_set_param_data_fifo_size(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 32, 32768)) { ++ DWC_WARN("Wrong value for data_fifo_size\n"); ++ DWC_WARN("data_fifo_size must be 32-32768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > core_if->hwcfg3.b.dfifo_depth) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->data_fifo_size)) { ++ DWC_ERROR ++ ("%d invalid for data_fifo_size parameter. Check HW configuration.\n", ++ val); ++ } ++ val = core_if->hwcfg3.b.dfifo_depth; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->data_fifo_size = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_data_fifo_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->data_fifo_size; ++} ++ ++int dwc_otg_set_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) { ++ DWC_WARN("Wrong value for dev_rx_fifo_size\n"); ++ DWC_WARN("dev_rx_fifo_size must be 16-32768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > dwc_read_reg32(&core_if->core_global_regs->grxfsiz)) { ++ if(dwc_otg_param_initialized(core_if->core_params->dev_rx_fifo_size)) { ++ DWC_WARN("%d invalid for dev_rx_fifo_size parameter\n", val); ++ } ++ val = dwc_read_reg32(&core_if->core_global_regs->grxfsiz); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->dev_rx_fifo_size = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->dev_rx_fifo_size; ++} ++ ++int dwc_otg_set_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) { ++ DWC_WARN("Wrong value for dev_nperio_tx_fifo\n"); ++ DWC_WARN("dev_nperio_tx_fifo must be 16-32768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->dev_nperio_tx_fifo_size)) { ++ DWC_ERROR ++ ("%d invalid for dev_nperio_tx_fifo_size. Check HW configuration.\n", ++ val); ++ } ++ val = ++ (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> ++ 16); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->dev_nperio_tx_fifo_size = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->dev_nperio_tx_fifo_size; ++} ++ ++int dwc_otg_set_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) { ++ DWC_WARN("Wrong value for host_rx_fifo_size\n"); ++ DWC_WARN("host_rx_fifo_size must be 16-32768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > dwc_read_reg32(&core_if->core_global_regs->grxfsiz)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->host_rx_fifo_size)) { ++ DWC_ERROR ++ ("%d invalid for host_rx_fifo_size. Check HW configuration.\n", ++ val); ++ } ++ val = dwc_read_reg32(&core_if->core_global_regs->grxfsiz); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->host_rx_fifo_size = val; ++ return retval; ++ ++} ++ ++int32_t dwc_otg_get_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->host_rx_fifo_size; ++} ++ ++int dwc_otg_set_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) { ++ DWC_WARN("Wrong value for host_nperio_tx_fifo_size\n"); ++ DWC_WARN("host_nperio_tx_fifo_size must be 16-32768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> 16)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->host_nperio_tx_fifo_size)) { ++ DWC_ERROR ++ ("%d invalid for host_nperio_tx_fifo_size. Check HW configuration.\n", ++ val); ++ } ++ val = ++ (dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz) >> ++ 16); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->host_nperio_tx_fifo_size = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->host_nperio_tx_fifo_size; ++} ++ ++int dwc_otg_set_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 16, 32768)) { ++ DWC_WARN("Wrong value for host_perio_tx_fifo_size\n"); ++ DWC_WARN("host_perio_tx_fifo_size must be 16-32768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > ++ ((dwc_read_reg32(&core_if->core_global_regs->hptxfsiz) >> 16))) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->host_perio_tx_fifo_size)) { ++ DWC_ERROR ++ ("%d invalid for host_perio_tx_fifo_size. Check HW configuration.\n", ++ val); ++ } ++ val = ++ (dwc_read_reg32(&core_if->core_global_regs->hptxfsiz) >> ++ 16); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->host_perio_tx_fifo_size = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->host_perio_tx_fifo_size; ++} ++ ++int dwc_otg_set_param_max_transfer_size(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 2047, 524288)) { ++ DWC_WARN("Wrong value for max_transfer_size\n"); ++ DWC_WARN("max_transfer_size must be 2047-524288\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val >= (1 << (core_if->hwcfg3.b.xfer_size_cntr_width + 11))) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->max_transfer_size)) { ++ DWC_ERROR ++ ("%d invalid for max_transfer_size. Check HW configuration.\n", ++ val); ++ } ++ val = ++ ((1 << (core_if->hwcfg3.b.packet_size_cntr_width + 11)) - ++ 1); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->max_transfer_size = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_max_transfer_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->max_transfer_size; ++} ++ ++int dwc_otg_set_param_max_packet_count(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 15, 511)) { ++ DWC_WARN("Wrong value for max_packet_count\n"); ++ DWC_WARN("max_packet_count must be 15-511\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > (1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4))) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->max_packet_count)) { ++ DWC_ERROR ++ ("%d invalid for max_packet_count. Check HW configuration.\n", ++ val); ++ } ++ val = ++ ((1 << (core_if->hwcfg3.b.packet_size_cntr_width + 4)) - 1); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->max_packet_count = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_max_packet_count(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->max_packet_count; ++} ++ ++int dwc_otg_set_param_host_channels(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 1, 16)) { ++ DWC_WARN("Wrong value for host_channels\n"); ++ DWC_WARN("host_channels must be 1-16\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > (core_if->hwcfg2.b.num_host_chan + 1)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->host_channels)) { ++ DWC_ERROR ++ ("%d invalid for host_channels. Check HW configurations.\n", ++ val); ++ } ++ val = (core_if->hwcfg2.b.num_host_chan + 1); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->host_channels = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_host_channels(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->host_channels; ++} ++ ++int dwc_otg_set_param_dev_endpoints(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 1, 15)) { ++ DWC_WARN("Wrong value for dev_endpoints\n"); ++ DWC_WARN("dev_endpoints must be 1-15\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > (core_if->hwcfg2.b.num_dev_ep)) { ++ if (dwc_otg_param_initialized ++ (core_if->core_params->dev_endpoints)) { ++ DWC_ERROR ++ ("%d invalid for dev_endpoints. Check HW configurations.\n", ++ val); ++ } ++ val = core_if->hwcfg2.b.num_dev_ep; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->dev_endpoints = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dev_endpoints(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->dev_endpoints; ++} ++ ++int dwc_otg_set_param_phy_type(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ int valid = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 2)) { ++ DWC_WARN("Wrong value for phy_type\n"); ++ DWC_WARN("phy_type must be 0,1 or 2\n"); ++ return -DWC_E_INVALID; ++ } ++#ifndef NO_FS_PHY_HW_CHECKS ++ if ((val == DWC_PHY_TYPE_PARAM_UTMI) && ++ ((core_if->hwcfg2.b.hs_phy_type == 1) || ++ (core_if->hwcfg2.b.hs_phy_type == 3))) { ++ valid = 1; ++ } else if ((val == DWC_PHY_TYPE_PARAM_ULPI) && ++ ((core_if->hwcfg2.b.hs_phy_type == 2) || ++ (core_if->hwcfg2.b.hs_phy_type == 3))) { ++ valid = 1; ++ } else if ((val == DWC_PHY_TYPE_PARAM_FS) && ++ (core_if->hwcfg2.b.fs_phy_type == 1)) { ++ valid = 1; ++ } ++ if (!valid) { ++ if (dwc_otg_param_initialized(core_if->core_params->phy_type)) { ++ DWC_ERROR ++ ("%d invalid for phy_type. Check HW configurations.\n", ++ val); ++ } ++ if (core_if->hwcfg2.b.hs_phy_type) { ++ if ((core_if->hwcfg2.b.hs_phy_type == 3) || ++ (core_if->hwcfg2.b.hs_phy_type == 1)) { ++ val = DWC_PHY_TYPE_PARAM_UTMI; ++ } else { ++ val = DWC_PHY_TYPE_PARAM_ULPI; ++ } ++ } ++ retval = -DWC_E_INVALID; ++ } ++#endif ++ core_if->core_params->phy_type = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_phy_type(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->phy_type; ++} ++ ++int dwc_otg_set_param_speed(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for speed parameter\n"); ++ DWC_WARN("max_speed parameter must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ if ((val == 0) ++ && dwc_otg_get_param_phy_type(core_if) == DWC_PHY_TYPE_PARAM_FS) { ++ if (dwc_otg_param_initialized(core_if->core_params->speed)) { ++ DWC_ERROR ++ ("%d invalid for speed paremter. Check HW configuration.\n", ++ val); ++ } ++ val = ++ (dwc_otg_get_param_phy_type(core_if) == ++ DWC_PHY_TYPE_PARAM_FS ? 1 : 0); ++ retval = -DWC_E_INVALID; ++ } ++ core_if->core_params->speed = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_speed(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->speed; ++} ++ ++int dwc_otg_set_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN ++ ("Wrong value for host_ls_low_power_phy_clk parameter\n"); ++ DWC_WARN("host_ls_low_power_phy_clk must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val == DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ) ++ && (dwc_otg_get_param_phy_type(core_if) == DWC_PHY_TYPE_PARAM_FS)) { ++ if(dwc_otg_param_initialized(core_if->core_params->host_ls_low_power_phy_clk)) { ++ DWC_ERROR("%d invalid for host_ls_low_power_phy_clk. Check HW configuration.\n", ++ val); ++ } ++ val = ++ (dwc_otg_get_param_phy_type(core_if) == ++ DWC_PHY_TYPE_PARAM_FS) ? ++ DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ : ++ DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->host_ls_low_power_phy_clk = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->host_ls_low_power_phy_clk; ++} ++ ++int dwc_otg_set_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for phy_ulpi_ddr\n"); ++ DWC_WARN("phy_upli_ddr must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->phy_ulpi_ddr = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->phy_ulpi_ddr; ++} ++ ++int dwc_otg_set_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong valaue for phy_ulpi_ext_vbus\n"); ++ DWC_WARN("phy_ulpi_ext_vbus must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->phy_ulpi_ext_vbus = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->phy_ulpi_ext_vbus; ++} ++ ++int dwc_otg_set_param_phy_utmi_width(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 8, 8) && DWC_OTG_PARAM_TEST(val, 16, 16)) { ++ DWC_WARN("Wrong valaue for phy_utmi_width\n"); ++ DWC_WARN("phy_utmi_width must be 8 or 16\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->phy_utmi_width = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_phy_utmi_width(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->phy_utmi_width; ++} ++ ++int dwc_otg_set_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong valaue for ulpi_fs_ls\n"); ++ DWC_WARN("ulpi_fs_ls must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->ulpi_fs_ls = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->ulpi_fs_ls; ++} ++ ++int dwc_otg_set_param_ts_dline(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong valaue for ts_dline\n"); ++ DWC_WARN("ts_dline must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->ts_dline = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_ts_dline(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->ts_dline; ++} ++ ++int dwc_otg_set_param_i2c_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong valaue for i2c_enable\n"); ++ DWC_WARN("i2c_enable must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++#ifndef NO_FS_PHY_HW_CHECK ++ if (val == 1 && core_if->hwcfg3.b.i2c == 0) { ++ if(dwc_otg_param_initialized(core_if->core_params->i2c_enable)) { ++ DWC_ERROR("%d invalid for i2c_enable. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++#endif ++ ++ core_if->core_params->i2c_enable = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_i2c_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->i2c_enable; ++} ++ ++int dwc_otg_set_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val, int fifo_num) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 4, 768)) { ++ DWC_WARN("Wrong value for dev_perio_tx_fifo_size\n"); ++ DWC_WARN("dev_perio_tx_fifo_size must be 4-768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > (dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[fifo_num]))) { ++ if(dwc_otg_param_initialized(core_if->core_params->dev_perio_tx_fifo_size[fifo_num])) { ++ DWC_ERROR("`%d' invalid for parameter `dev_perio_fifo_size_%d'. Check HW configuration.\n", ++ val, fifo_num); ++ } ++ val = (dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[fifo_num])); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->dev_perio_tx_fifo_size[fifo_num] = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int fifo_num) ++{ ++ return core_if->core_params->dev_perio_tx_fifo_size[fifo_num]; ++} ++ ++int dwc_otg_set_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong valaue for en_multiple_tx_fifo,\n"); ++ DWC_WARN("en_multiple_tx_fifo must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val == 1 && core_if->hwcfg4.b.ded_fifo_en == 0) { ++ if(dwc_otg_param_initialized(core_if->core_params->en_multiple_tx_fifo)) { ++ DWC_ERROR("%d invalid for parameter en_multiple_tx_fifo. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->en_multiple_tx_fifo = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->en_multiple_tx_fifo; ++} ++ ++int dwc_otg_set_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if, int32_t val, ++ int fifo_num) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 4, 768)) { ++ DWC_WARN("Wrong value for dev_tx_fifo_size\n"); ++ DWC_WARN("dev_tx_fifo_size must be 4-768\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val > (dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[fifo_num]))) { ++ if(dwc_otg_param_initialized(core_if->core_params->dev_tx_fifo_size[fifo_num])) { ++ DWC_ERROR("`%d' invalid for parameter `dev_tx_fifo_size_%d'. Check HW configuration.\n", ++ val, fifo_num); ++ } ++ val = (dwc_read_reg32(&core_if->core_global_regs->dptxfsiz_dieptxf[fifo_num])); ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->dev_tx_fifo_size[fifo_num] = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int fifo_num) ++{ ++ return core_if->core_params->dev_tx_fifo_size[fifo_num]; ++} ++ ++int dwc_otg_set_param_thr_ctl(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 7)) { ++ DWC_WARN("Wrong value for thr_ctl\n"); ++ DWC_WARN("thr_ctl must be 0-7\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if ((val != 0) && ++ (!dwc_otg_get_param_dma_enable(core_if) || ++ !core_if->hwcfg4.b.ded_fifo_en)) { ++ if(dwc_otg_param_initialized(core_if->core_params->thr_ctl)) { ++ DWC_ERROR("%d invalid for parameter thr_ctl. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->thr_ctl = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_thr_ctl(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->thr_ctl; ++} ++ ++int dwc_otg_set_param_lpm_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("Wrong value for lpm_enable\n"); ++ DWC_WARN("lpm_enable must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val && !core_if->hwcfg3.b.otg_lpm_en) { ++ if(dwc_otg_param_initialized(core_if->core_params->lpm_enable)) { ++ DWC_ERROR("%d invalid for parameter lpm_enable. Check HW configuration.\n", ++ val); ++ } ++ val = 0; ++ retval = -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->lpm_enable = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_lpm_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->lpm_enable; ++} ++ ++int dwc_otg_set_param_tx_thr_length(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 8, 128)) { ++ DWC_WARN("Wrong valaue for tx_thr_length\n"); ++ DWC_WARN("tx_thr_length must be 8 - 128\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->tx_thr_length = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_tx_thr_length(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->tx_thr_length; ++} ++ ++int dwc_otg_set_param_rx_thr_length(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 8, 128)) { ++ DWC_WARN("Wrong valaue for rx_thr_length\n"); ++ DWC_WARN("rx_thr_length must be 8 - 128\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ core_if->core_params->rx_thr_length = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_rx_thr_length(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->rx_thr_length; ++} ++ ++int dwc_otg_set_param_dma_burst_size(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ if (DWC_OTG_PARAM_TEST(val, 1, 1) && ++ DWC_OTG_PARAM_TEST(val, 4, 4) && ++ DWC_OTG_PARAM_TEST(val, 8, 8) && ++ DWC_OTG_PARAM_TEST(val, 16, 16) && ++ DWC_OTG_PARAM_TEST(val, 32, 32) && ++ DWC_OTG_PARAM_TEST(val, 64, 64) && ++ DWC_OTG_PARAM_TEST(val, 128, 128) && ++ DWC_OTG_PARAM_TEST(val, 256, 256)) { ++ DWC_WARN("`%d' invalid for parameter `dma_burst_size'\n", val); ++ return -DWC_E_INVALID; ++ } ++ core_if->core_params->dma_burst_size = val; ++ return 0; ++} ++ ++int32_t dwc_otg_get_param_dma_burst_size(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->dma_burst_size; ++} ++ ++int dwc_otg_set_param_pti_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("`%d' invalid for parameter `pti_enable'\n", val); ++ return -DWC_E_INVALID; ++ } ++ if (val && (core_if->snpsid < OTG_CORE_REV_2_72a)) { ++ if (dwc_otg_param_initialized(core_if->core_params->pti_enable)) { ++ DWC_ERROR("%d invalid for parameter pti_enable. Check HW configuration.\n", ++ val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ core_if->core_params->pti_enable = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_pti_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->pti_enable; ++} ++ ++int dwc_otg_set_param_mpi_enable(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("`%d' invalid for parameter `mpi_enable'\n", val); ++ return -DWC_E_INVALID; ++ } ++ if (val && (core_if->hwcfg2.b.multi_proc_int == 0)) { ++ if (dwc_otg_param_initialized(core_if->core_params->mpi_enable)) { ++ DWC_ERROR("%d invalid for parameter mpi_enable. Check HW configuration.\n", ++ val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ core_if->core_params->mpi_enable = val; ++ return retval; ++} ++ ++int32_t dwc_otg_get_param_mpi_enable(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->mpi_enable; ++} ++ ++int dwc_otg_set_param_ic_usb_cap(dwc_otg_core_if_t * core_if, ++ int32_t val) ++{ ++ int retval = 0; ++ if (DWC_OTG_PARAM_TEST(val, 0, 1)) { ++ DWC_WARN("`%d' invalid for parameter `ic_usb_cap'\n", val); ++ DWC_WARN("ic_usb_cap must be 0 or 1\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (val && (core_if->hwcfg3.b.otg_enable_ic_usb == 0)) { ++ if (dwc_otg_param_initialized(core_if->core_params->ic_usb_cap)) { ++ DWC_ERROR("%d invalid for parameter ic_usb_cap. Check HW configuration.\n", ++ val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ core_if->core_params->ic_usb_cap = val; ++ return retval; ++} ++int32_t dwc_otg_get_param_ic_usb_cap(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->ic_usb_cap; ++} ++ ++int dwc_otg_set_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if, int32_t val) ++{ ++ int retval = 0; ++ int valid = 1; ++ ++ if(DWC_OTG_PARAM_TEST(val, 0, 3)) { ++ DWC_WARN("`%d' invalid for parameter `ahb_thr_ratio'\n", val); ++ DWC_WARN("ahb_thr_ratio must be 0 - 3\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if(val && (core_if->snpsid < OTG_CORE_REV_2_81a || !dwc_otg_get_param_thr_ctl(core_if))) { ++ valid = 0; ++ } else if(val && ((dwc_otg_get_param_tx_thr_length(core_if) / (1 << val)) < 4)) { ++ valid = 0; ++ } ++ if(valid == 0) { ++ if(dwc_otg_param_initialized(core_if->core_params->ahb_thr_ratio)) { ++ DWC_ERROR("%d invalid for parameter ahb_thr_ratio. Chack HW configuration.\n", val); ++ } ++ retval = -DWC_E_INVALID; ++ val = 0; ++ } ++ ++ core_if->core_params->ahb_thr_ratio = val; ++ return retval; ++} ++int32_t dwc_otg_get_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->core_params->ahb_thr_ratio; ++} ++ ++ ++uint32_t dwc_otg_get_hnpstatus(dwc_otg_core_if_t * core_if) ++{ ++ gotgctl_data_t otgctl; ++ otgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl); ++ return otgctl.b.hstnegscs; ++} ++ ++uint32_t dwc_otg_get_srpstatus(dwc_otg_core_if_t * core_if) ++{ ++ gotgctl_data_t otgctl; ++ otgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl); ++ return otgctl.b.sesreqscs; ++} ++ ++void dwc_otg_set_hnpreq(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ gotgctl_data_t otgctl; ++ otgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl); ++ otgctl.b.hnpreq = val; ++ dwc_write_reg32(&core_if->core_global_regs->gotgctl, otgctl.d32); ++} ++ ++uint32_t dwc_otg_get_gsnpsid(dwc_otg_core_if_t * core_if) ++{ ++ return core_if->snpsid; ++} ++ ++uint32_t dwc_otg_get_mode(dwc_otg_core_if_t * core_if) ++{ ++ gotgctl_data_t otgctl; ++ otgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl); ++ return otgctl.b.currmod; ++} ++ ++uint32_t dwc_otg_get_hnpcapable(dwc_otg_core_if_t * core_if) ++{ ++ gusbcfg_data_t usbcfg; ++ usbcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->gusbcfg); ++ return usbcfg.b.hnpcap; ++} ++ ++void dwc_otg_set_hnpcapable(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ gusbcfg_data_t usbcfg; ++ usbcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->gusbcfg); ++ usbcfg.b.hnpcap = val; ++ dwc_write_reg32(&core_if->core_global_regs->gusbcfg, usbcfg.d32); ++} ++ ++uint32_t dwc_otg_get_srpcapable(dwc_otg_core_if_t * core_if) ++{ ++ gusbcfg_data_t usbcfg; ++ usbcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->gusbcfg); ++ return usbcfg.b.srpcap; ++} ++ ++void dwc_otg_set_srpcapable(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ gusbcfg_data_t usbcfg; ++ usbcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->gusbcfg); ++ usbcfg.b.srpcap = val; ++ dwc_write_reg32(&core_if->core_global_regs->gusbcfg, usbcfg.d32); ++} ++ ++uint32_t dwc_otg_get_devspeed(dwc_otg_core_if_t * core_if) ++{ ++ dcfg_data_t dcfg; ++ dcfg.d32 = -1; //GRAYG ++ DWC_DEBUGPL(DBG_CILV, "%s - core_if(%p)\n", __func__, core_if); ++ if (NULL == core_if) ++ DWC_ERROR("reg request with NULL core_if\n"); ++ DWC_DEBUGPL(DBG_CILV, "%s - core_if(%p)->dev_if(%p)\n", __func__, ++ core_if, core_if->dev_if); ++ if (NULL == core_if->dev_if) ++ DWC_ERROR("reg request with NULL dev_if\n"); ++ DWC_DEBUGPL(DBG_CILV, "%s - core_if(%p)->dev_if(%p)->" ++ "dev_global_regs(%p)\n", __func__, ++ core_if, core_if->dev_if, ++ core_if->dev_if->dev_global_regs); ++ if (NULL == core_if->dev_if->dev_global_regs) ++ DWC_ERROR("reg request with NULL dev_global_regs\n"); ++ else { ++ DWC_DEBUGPL(DBG_CILV, "%s - &core_if(%p)->dev_if(%p)->" ++ "dev_global_regs(%p)->dcfg = %p\n", __func__, ++ core_if, core_if->dev_if, ++ core_if->dev_if->dev_global_regs, ++ &core_if->dev_if->dev_global_regs->dcfg); ++ dcfg.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dcfg); ++ } ++ return dcfg.b.devspd; ++} ++ ++void dwc_otg_set_devspeed(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ dcfg_data_t dcfg; ++ dcfg.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dcfg); ++ dcfg.b.devspd = val; ++ dwc_write_reg32(&core_if->dev_if->dev_global_regs->dcfg, dcfg.d32); ++} ++ ++uint32_t dwc_otg_get_busconnected(dwc_otg_core_if_t * core_if) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = dwc_read_reg32(core_if->host_if->hprt0); ++ return hprt0.b.prtconnsts; ++} ++ ++uint32_t dwc_otg_get_enumspeed(dwc_otg_core_if_t * core_if) ++{ ++ dsts_data_t dsts; ++ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ return dsts.b.enumspd; ++} ++ ++uint32_t dwc_otg_get_prtpower(dwc_otg_core_if_t * core_if) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = dwc_read_reg32(core_if->host_if->hprt0); ++ return hprt0.b.prtpwr; ++ ++} ++ ++void dwc_otg_set_prtpower(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = dwc_read_reg32(core_if->host_if->hprt0); ++ hprt0.b.prtpwr = val; ++ dwc_write_reg32(core_if->host_if->hprt0, val); ++} ++ ++uint32_t dwc_otg_get_prtsuspend(dwc_otg_core_if_t * core_if) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = dwc_read_reg32(core_if->host_if->hprt0); ++ return hprt0.b.prtsusp; ++ ++} ++ ++void dwc_otg_set_prtsuspend(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = dwc_read_reg32(core_if->host_if->hprt0); ++ hprt0.b.prtsusp = val; ++ dwc_write_reg32(core_if->host_if->hprt0, val); ++} ++ ++void dwc_otg_set_prtresume(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = dwc_read_reg32(core_if->host_if->hprt0); ++ hprt0.b.prtres = val; ++ dwc_write_reg32(core_if->host_if->hprt0, val); ++} ++ ++uint32_t dwc_otg_get_remotewakesig(dwc_otg_core_if_t * core_if) ++{ ++ dctl_data_t dctl; ++ dctl.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dctl); ++ return dctl.b.rmtwkupsig; ++} ++ ++uint32_t dwc_otg_get_lpm_portsleepstatus(dwc_otg_core_if_t * core_if) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ ++ DWC_ASSERT(! ++ ((core_if->lx_state == DWC_OTG_L1) ^ lpmcfg.b.prt_sleep_sts), ++ "lx_state = %d, lmpcfg.prt_sleep_sts = %d\n", ++ core_if->lx_state, lpmcfg.b.prt_sleep_sts); ++ ++ return lpmcfg.b.prt_sleep_sts; ++} ++ ++uint32_t dwc_otg_get_lpm_remotewakeenabled(dwc_otg_core_if_t * core_if) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ return lpmcfg.b.rem_wkup_en; ++} ++ ++uint32_t dwc_otg_get_lpmresponse(dwc_otg_core_if_t * core_if) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ return lpmcfg.b.appl_resp; ++} ++ ++void dwc_otg_set_lpmresponse(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ lpmcfg.b.appl_resp = val; ++ dwc_write_reg32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32); ++} ++ ++uint32_t dwc_otg_get_hsic_connect(dwc_otg_core_if_t * core_if) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ return lpmcfg.b.hsic_connect; ++} ++ ++void dwc_otg_set_hsic_connect(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ lpmcfg.b.hsic_connect = val; ++ dwc_write_reg32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32); ++} ++ ++uint32_t dwc_otg_get_inv_sel_hsic(dwc_otg_core_if_t * core_if) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ return lpmcfg.b.inv_sel_hsic; ++ ++} ++ ++void dwc_otg_set_inv_sel_hsic(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ lpmcfg.b.inv_sel_hsic = val; ++ dwc_write_reg32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32); ++} ++ ++uint32_t dwc_otg_get_gotgctl(dwc_otg_core_if_t * core_if) ++{ ++ return dwc_read_reg32(&core_if->core_global_regs->gotgctl); ++} ++ ++void dwc_otg_set_gotgctl(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ dwc_write_reg32(&core_if->core_global_regs->gotgctl, val); ++} ++ ++uint32_t dwc_otg_get_gusbcfg(dwc_otg_core_if_t * core_if) ++{ ++ return dwc_read_reg32(&core_if->core_global_regs->gusbcfg); ++} ++ ++void dwc_otg_set_gusbcfg(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ dwc_write_reg32(&core_if->core_global_regs->gusbcfg, val); ++} ++ ++uint32_t dwc_otg_get_grxfsiz(dwc_otg_core_if_t * core_if) ++{ ++ return dwc_read_reg32(&core_if->core_global_regs->grxfsiz); ++} ++ ++void dwc_otg_set_grxfsiz(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ dwc_write_reg32(&core_if->core_global_regs->grxfsiz, val); ++} ++ ++uint32_t dwc_otg_get_gnptxfsiz(dwc_otg_core_if_t * core_if) ++{ ++ return dwc_read_reg32(&core_if->core_global_regs->gnptxfsiz); ++} ++ ++void dwc_otg_set_gnptxfsiz(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ dwc_write_reg32(&core_if->core_global_regs->gnptxfsiz, val); ++} ++ ++uint32_t dwc_otg_get_gpvndctl(dwc_otg_core_if_t * core_if) ++{ ++ return dwc_read_reg32(&core_if->core_global_regs->gpvndctl); ++} ++ ++void dwc_otg_set_gpvndctl(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ dwc_write_reg32(&core_if->core_global_regs->gpvndctl, val); ++} ++ ++uint32_t dwc_otg_get_ggpio(dwc_otg_core_if_t * core_if) ++{ ++ return dwc_read_reg32(&core_if->core_global_regs->ggpio); ++} ++ ++void dwc_otg_set_ggpio(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ dwc_write_reg32(&core_if->core_global_regs->ggpio, val); ++} ++ ++uint32_t dwc_otg_get_hprt0(dwc_otg_core_if_t * core_if) ++{ ++ return dwc_read_reg32(core_if->host_if->hprt0); ++ ++} ++ ++void dwc_otg_set_hprt0(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ dwc_write_reg32(core_if->host_if->hprt0, val); ++} ++ ++uint32_t dwc_otg_get_guid(dwc_otg_core_if_t * core_if) ++{ ++ return dwc_read_reg32(&core_if->core_global_regs->guid); ++} ++ ++void dwc_otg_set_guid(dwc_otg_core_if_t * core_if, uint32_t val) ++{ ++ dwc_write_reg32(&core_if->core_global_regs->guid, val); ++} ++ ++uint32_t dwc_otg_get_hptxfsiz(dwc_otg_core_if_t * core_if) ++{ ++ return dwc_read_reg32(&core_if->core_global_regs->hptxfsiz); ++} +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_cil.h +@@ -0,0 +1,1143 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil.h $ ++ * $Revision: #99 $ ++ * $Date: 2009/04/21 $ ++ * $Change: 1237466 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#if !defined(__DWC_CIL_H__) ++#define __DWC_CIL_H__ ++ ++//#define HW2937_WORKAROUND ++#define DBG_HW2937 0x400 ++ ++#include "dwc_os.h" ++#include "dwc_list.h" ++#include "dwc_otg_dbg.h" ++#include "dwc_otg_regs.h" ++ ++#include "dwc_otg_core_if.h" ++ ++/** ++ * @file ++ * This file contains the interface to the Core Interface Layer. ++ */ ++ ++#ifdef DWC_UTE_CFI ++ ++#define MAX_DMA_DESCS_PER_EP 256 ++ ++/** ++ * Enumeration for the data buffer mode ++ */ ++typedef enum _data_buffer_mode { ++ BM_STANDARD = 0, /* data buffer is in normal mode */ ++ BM_SG = 1, /* data buffer uses the scatter/gather mode */ ++ BM_CONCAT = 2, /* data buffer uses the concatenation mode */ ++ BM_CIRCULAR = 3, /* data buffer uses the circular DMA mode */ ++ BM_ALIGN = 4 /* data buffer is in buffer alignment mode */ ++} data_buffer_mode_e; ++#endif //DWC_UTE_CFI ++ ++/** Macros defined for DWC OTG HW Release verison */ ++ ++#define OTG_CORE_REV_2_60a 0x4F54260A ++#define OTG_CORE_REV_2_71a 0x4F54271A ++#define OTG_CORE_REV_2_72a 0x4F54272A ++#define OTG_CORE_REV_2_80a 0x4F54280A ++#define OTG_CORE_REV_2_81a 0x4F54281A ++#define OTG_CORE_REV_2_90a 0x4F54290A ++ ++/** ++ * Information for each ISOC packet. ++ */ ++typedef struct iso_pkt_info { ++ uint32_t offset; ++ uint32_t length; ++ int32_t status; ++} iso_pkt_info_t; ++ ++/** ++ * The <code>dwc_ep</code> structure represents the state of a single ++ * endpoint when acting in device mode. It contains the data items ++ * needed for an endpoint to be activated and transfer packets. ++ */ ++typedef struct dwc_ep { ++ /** EP number used for register address lookup */ ++ uint8_t num; ++ /** EP direction 0 = OUT */ ++ unsigned is_in:1; ++ /** EP active. */ ++ unsigned active:1; ++ ++ /** Periodic Tx FIFO # for IN EPs For INTR EP set to 0 to use non-periodic Tx FIFO ++ If dedicated Tx FIFOs are enabled for all IN Eps - Tx FIFO # FOR IN EPs*/ ++ unsigned tx_fifo_num:4; ++ /** EP type: 0 - Control, 1 - ISOC, 2 - BULK, 3 - INTR */ ++ unsigned type:2; ++#define DWC_OTG_EP_TYPE_CONTROL 0 ++#define DWC_OTG_EP_TYPE_ISOC 1 ++#define DWC_OTG_EP_TYPE_BULK 2 ++#define DWC_OTG_EP_TYPE_INTR 3 ++ ++ /** DATA start PID for INTR and BULK EP */ ++ unsigned data_pid_start:1; ++ /** Frame (even/odd) for ISOC EP */ ++ unsigned even_odd_frame:1; ++ /** Max Packet bytes */ ++ unsigned maxpacket:11; ++ ++ /** Max Transfer size */ ++ uint32_t maxxfer; ++ ++ /** @name Transfer state */ ++ /** @{ */ ++ ++ /** ++ * Pointer to the beginning of the transfer buffer -- do not modify ++ * during transfer. ++ */ ++ ++ dwc_dma_t dma_addr; ++ ++ dwc_dma_t dma_desc_addr; ++ dwc_otg_dev_dma_desc_t *desc_addr; ++ ++ uint8_t *start_xfer_buff; ++ /** pointer to the transfer buffer */ ++ uint8_t *xfer_buff; ++ /** Number of bytes to transfer */ ++ unsigned xfer_len:19; ++ /** Number of bytes transferred. */ ++ unsigned xfer_count:19; ++ /** Sent ZLP */ ++ unsigned sent_zlp:1; ++ /** Total len for control transfer */ ++ unsigned total_len:19; ++ ++ /** stall clear flag */ ++ unsigned stall_clear_flag:1; ++ ++#ifdef DWC_UTE_CFI ++ /* The buffer mode */ ++ data_buffer_mode_e buff_mode; ++ ++ /* The chain of DMA descriptors. ++ * MAX_DMA_DESCS_PER_EP will be allocated for each active EP. ++ */ ++ dwc_otg_dma_desc_t *descs; ++ ++ /* The DMA address of the descriptors chain start */ ++ dma_addr_t descs_dma_addr; ++ /** This variable stores the length of the last enqueued request */ ++ uint32_t cfi_req_len; ++#endif //DWC_UTE_CFI ++ ++ /** Allocated DMA Desc count */ ++ uint32_t desc_cnt; ++ ++#ifdef DWC_EN_ISOC ++ /** ++ * Variables specific for ISOC EPs ++ * ++ */ ++ /** DMA addresses of ISOC buffers */ ++ dwc_dma_t dma_addr0; ++ dwc_dma_t dma_addr1; ++ ++ dwc_dma_t iso_dma_desc_addr; ++ dwc_otg_dev_dma_desc_t *iso_desc_addr; ++ ++ /** pointer to the transfer buffers */ ++ uint8_t *xfer_buff0; ++ uint8_t *xfer_buff1; ++ ++ /** number of ISOC Buffer is processing */ ++ uint32_t proc_buf_num; ++ /** Interval of ISOC Buffer processing */ ++ uint32_t buf_proc_intrvl; ++ /** Data size for regular frame */ ++ uint32_t data_per_frame; ++ ++ /* todo - pattern data support is to be implemented in the future */ ++ /** Data size for pattern frame */ ++ uint32_t data_pattern_frame; ++ /** Frame number of pattern data */ ++ uint32_t sync_frame; ++ ++ /** bInterval */ ++ uint32_t bInterval; ++ /** ISO Packet number per frame */ ++ uint32_t pkt_per_frm; ++ /** Next frame num for which will be setup DMA Desc */ ++ uint32_t next_frame; ++ /** Number of packets per buffer processing */ ++ uint32_t pkt_cnt; ++ /** Info for all isoc packets */ ++ iso_pkt_info_t *pkt_info; ++ /** current pkt number */ ++ uint32_t cur_pkt; ++ /** current pkt number */ ++ uint8_t *cur_pkt_addr; ++ /** current pkt number */ ++ uint32_t cur_pkt_dma_addr; ++#endif /* DWC_EN_ISOC */ ++ ++/** @} */ ++} dwc_ep_t; ++ ++/* ++ * Reasons for halting a host channel. ++ */ ++typedef enum dwc_otg_halt_status { ++ DWC_OTG_HC_XFER_NO_HALT_STATUS, ++ DWC_OTG_HC_XFER_COMPLETE, ++ DWC_OTG_HC_XFER_URB_COMPLETE, ++ DWC_OTG_HC_XFER_ACK, ++ DWC_OTG_HC_XFER_NAK, ++ DWC_OTG_HC_XFER_NYET, ++ DWC_OTG_HC_XFER_STALL, ++ DWC_OTG_HC_XFER_XACT_ERR, ++ DWC_OTG_HC_XFER_FRAME_OVERRUN, ++ DWC_OTG_HC_XFER_BABBLE_ERR, ++ DWC_OTG_HC_XFER_DATA_TOGGLE_ERR, ++ DWC_OTG_HC_XFER_AHB_ERR, ++ DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE, ++ DWC_OTG_HC_XFER_URB_DEQUEUE ++#ifdef HW2937_WORKAROUND ++ , DWC_OTG_HC_XFER_PAUSE_IN ++#endif ++} dwc_otg_halt_status_e; ++ ++/** ++ * Host channel descriptor. This structure represents the state of a single ++ * host channel when acting in host mode. It contains the data items needed to ++ * transfer packets to an endpoint via a host channel. ++ */ ++typedef struct dwc_hc { ++ /** Host channel number used for register address lookup */ ++ uint8_t hc_num; ++ ++ /** Device to access */ ++ unsigned dev_addr:7; ++ ++ /** EP to access */ ++ unsigned ep_num:4; ++ ++ /** EP direction. 0: OUT, 1: IN */ ++ unsigned ep_is_in:1; ++ ++ /** ++ * EP speed. ++ * One of the following values: ++ * - DWC_OTG_EP_SPEED_LOW ++ * - DWC_OTG_EP_SPEED_FULL ++ * - DWC_OTG_EP_SPEED_HIGH ++ */ ++ unsigned speed:2; ++#define DWC_OTG_EP_SPEED_LOW 0 ++#define DWC_OTG_EP_SPEED_FULL 1 ++#define DWC_OTG_EP_SPEED_HIGH 2 ++ ++ /** ++ * Endpoint type. ++ * One of the following values: ++ * - DWC_OTG_EP_TYPE_CONTROL: 0 ++ * - DWC_OTG_EP_TYPE_ISOC: 1 ++ * - DWC_OTG_EP_TYPE_BULK: 2 ++ * - DWC_OTG_EP_TYPE_INTR: 3 ++ */ ++ unsigned ep_type:2; ++ ++ /** Max packet size in bytes */ ++ unsigned max_packet:11; ++ ++ /** ++ * PID for initial transaction. ++ * 0: DATA0,<br> ++ * 1: DATA2,<br> ++ * 2: DATA1,<br> ++ * 3: MDATA (non-Control EP), ++ * SETUP (Control EP) ++ */ ++ unsigned data_pid_start:2; ++#define DWC_OTG_HC_PID_DATA0 0 ++#define DWC_OTG_HC_PID_DATA2 1 ++#define DWC_OTG_HC_PID_DATA1 2 ++#define DWC_OTG_HC_PID_MDATA 3 ++#define DWC_OTG_HC_PID_SETUP 3 ++ ++ /** Number of periodic transactions per (micro)frame */ ++ unsigned multi_count:2; ++ ++ /** @name Transfer State */ ++ /** @{ */ ++ ++ /** Pointer to the current transfer buffer position. */ ++ uint8_t *xfer_buff; ++ /** ++ * In Buffer DMA mode this buffer will be used ++ * if xfer_buff is not DWORD aligned. ++ */ ++ dwc_dma_t align_buff; ++ /** Total number of bytes to transfer. */ ++ uint32_t xfer_len; ++ /** Number of bytes transferred so far. */ ++ uint32_t xfer_count; ++ /** Packet count at start of transfer.*/ ++ uint16_t start_pkt_count; ++ ++ /** ++ * Flag to indicate whether the transfer has been started. Set to 1 if ++ * it has been started, 0 otherwise. ++ */ ++ uint8_t xfer_started; ++ ++ /** ++ * Set to 1 to indicate that a PING request should be issued on this ++ * channel. If 0, process normally. ++ */ ++ uint8_t do_ping; ++ ++ /** ++ * Set to 1 to indicate that the error count for this transaction is ++ * non-zero. Set to 0 if the error count is 0. ++ */ ++ uint8_t error_state; ++ ++ /** ++ * Set to 1 to indicate that this channel should be halted the next ++ * time a request is queued for the channel. This is necessary in ++ * slave mode if no request queue space is available when an attempt ++ * is made to halt the channel. ++ */ ++ uint8_t halt_on_queue; ++ ++ /** ++ * Set to 1 if the host channel has been halted, but the core is not ++ * finished flushing queued requests. Otherwise 0. ++ */ ++ uint8_t halt_pending; ++ ++ /** ++ * Reason for halting the host channel. ++ */ ++ dwc_otg_halt_status_e halt_status; ++ ++ /* ++ * Split settings for the host channel ++ */ ++ uint8_t do_split; /**< Enable split for the channel */ ++ uint8_t complete_split; /**< Enable complete split */ ++ uint8_t hub_addr; /**< Address of high speed hub */ ++ ++ uint8_t port_addr; /**< Port of the low/full speed device */ ++ /** Split transaction position ++ * One of the following values: ++ * - DWC_HCSPLIT_XACTPOS_MID ++ * - DWC_HCSPLIT_XACTPOS_BEGIN ++ * - DWC_HCSPLIT_XACTPOS_END ++ * - DWC_HCSPLIT_XACTPOS_ALL */ ++ uint8_t xact_pos; ++ ++ /** Set when the host channel does a short read. */ ++ uint8_t short_read; ++ ++ /** ++ * Number of requests issued for this channel since it was assigned to ++ * the current transfer (not counting PINGs). ++ */ ++ uint8_t requests; ++ ++ /** ++ * Queue Head for the transfer being processed by this channel. ++ */ ++ struct dwc_otg_qh *qh; ++ ++ /** @} */ ++ ++ /** Entry in list of host channels. */ ++ DWC_CIRCLEQ_ENTRY(dwc_hc) hc_list_entry; ++ ++ /** @name Descriptor DMA support */ ++ /** @{ */ ++ ++ /** Number of Transfer Descriptors */ ++ uint16_t ntd; ++ ++ /** Descriptor List DMA address */ ++ dwc_dma_t desc_list_addr; ++ ++ /** Scheduling micro-frame bitmap. */ ++ uint8_t schinfo; ++ ++ /** @} */ ++} dwc_hc_t; ++ ++/** ++ * The following parameters may be specified when starting the module. These ++ * parameters define how the DWC_otg controller should be configured. ++ */ ++typedef struct dwc_otg_core_params { ++ int32_t opt; ++ ++ /** ++ * Specifies the OTG capabilities. The driver will automatically ++ * detect the value for this parameter if none is specified. ++ * 0 - HNP and SRP capable (default) ++ * 1 - SRP Only capable ++ * 2 - No HNP/SRP capable ++ */ ++ int32_t otg_cap; ++ ++ /** ++ * Specifies whether to use slave or DMA mode for accessing the data ++ * FIFOs. The driver will automatically detect the value for this ++ * parameter if none is specified. ++ * 0 - Slave ++ * 1 - DMA (default, if available) ++ */ ++ int32_t dma_enable; ++ ++ /** ++ * When DMA mode is enabled specifies whether to use address DMA or DMA Descritor mode for accessing the data ++ * FIFOs in device mode. The driver will automatically detect the value for this ++ * parameter if none is specified. ++ * 0 - address DMA ++ * 1 - DMA Descriptor(default, if available) ++ */ ++ int32_t dma_desc_enable; ++ /** The DMA Burst size (applicable only for External DMA ++ * Mode). 1, 4, 8 16, 32, 64, 128, 256 (default 32) ++ */ ++ int32_t dma_burst_size; /* Translate this to GAHBCFG values */ ++ ++ /** ++ * Specifies the maximum speed of operation in host and device mode. ++ * The actual speed depends on the speed of the attached device and ++ * the value of phy_type. The actual speed depends on the speed of the ++ * attached device. ++ * 0 - High Speed (default) ++ * 1 - Full Speed ++ */ ++ int32_t speed; ++ /** Specifies whether low power mode is supported when attached ++ * to a Full Speed or Low Speed device in host mode. ++ * 0 - Don't support low power mode (default) ++ * 1 - Support low power mode ++ */ ++ int32_t host_support_fs_ls_low_power; ++ ++ /** Specifies the PHY clock rate in low power mode when connected to a ++ * Low Speed device in host mode. This parameter is applicable only if ++ * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS ++ * then defaults to 6 MHZ otherwise 48 MHZ. ++ * ++ * 0 - 48 MHz ++ * 1 - 6 MHz ++ */ ++ int32_t host_ls_low_power_phy_clk; ++ ++ /** ++ * 0 - Use cC FIFO size parameters ++ * 1 - Allow dynamic FIFO sizing (default) ++ */ ++ int32_t enable_dynamic_fifo; ++ ++ /** Total number of 4-byte words in the data FIFO memory. This ++ * memory includes the Rx FIFO, non-periodic Tx FIFO, and periodic ++ * Tx FIFOs. ++ * 32 to 32768 (default 8192) ++ * Note: The total FIFO memory depth in the FPGA configuration is 8192. ++ */ ++ int32_t data_fifo_size; ++ ++ /** Number of 4-byte words in the Rx FIFO in device mode when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1064) ++ */ ++ int32_t dev_rx_fifo_size; ++ ++ /** Number of 4-byte words in the non-periodic Tx FIFO in device mode ++ * when dynamic FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t dev_nperio_tx_fifo_size; ++ ++ /** Number of 4-byte words in each of the periodic Tx FIFOs in device ++ * mode when dynamic FIFO sizing is enabled. ++ * 4 to 768 (default 256) ++ */ ++ uint32_t dev_perio_tx_fifo_size[MAX_PERIO_FIFOS]; ++ ++ /** Number of 4-byte words in the Rx FIFO in host mode when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t host_rx_fifo_size; ++ ++ /** Number of 4-byte words in the non-periodic Tx FIFO in host mode ++ * when Dynamic FIFO sizing is enabled in the core. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t host_nperio_tx_fifo_size; ++ ++ /** Number of 4-byte words in the host periodic Tx FIFO when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++ int32_t host_perio_tx_fifo_size; ++ ++ /** The maximum transfer size supported in bytes. ++ * 2047 to 65,535 (default 65,535) ++ */ ++ int32_t max_transfer_size; ++ ++ /** The maximum number of packets in a transfer. ++ * 15 to 511 (default 511) ++ */ ++ int32_t max_packet_count; ++ ++ /** The number of host channel registers to use. ++ * 1 to 16 (default 12) ++ * Note: The FPGA configuration supports a maximum of 12 host channels. ++ */ ++ int32_t host_channels; ++ ++ /** The number of endpoints in addition to EP0 available for device ++ * mode operations. ++ * 1 to 15 (default 6 IN and OUT) ++ * Note: The FPGA configuration supports a maximum of 6 IN and OUT ++ * endpoints in addition to EP0. ++ */ ++ int32_t dev_endpoints; ++ ++ /** ++ * Specifies the type of PHY interface to use. By default, the driver ++ * will automatically detect the phy_type. ++ * ++ * 0 - Full Speed PHY ++ * 1 - UTMI+ (default) ++ * 2 - ULPI ++ */ ++ int32_t phy_type; ++ ++ /** ++ * Specifies the UTMI+ Data Width. This parameter is ++ * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI ++ * PHY_TYPE, this parameter indicates the data width between ++ * the MAC and the ULPI Wrapper.) Also, this parameter is ++ * applicable only if the OTG_HSPHY_WIDTH cC parameter was set ++ * to "8 and 16 bits", meaning that the core has been ++ * configured to work at either data path width. ++ * ++ * 8 or 16 bits (default 16) ++ */ ++ int32_t phy_utmi_width; ++ ++ /** ++ * Specifies whether the ULPI operates at double or single ++ * data rate. This parameter is only applicable if PHY_TYPE is ++ * ULPI. ++ * ++ * 0 - single data rate ULPI interface with 8 bit wide data ++ * bus (default) ++ * 1 - double data rate ULPI interface with 4 bit wide data ++ * bus ++ */ ++ int32_t phy_ulpi_ddr; ++ ++ /** ++ * Specifies whether to use the internal or external supply to ++ * drive the vbus with a ULPI phy. ++ */ ++ int32_t phy_ulpi_ext_vbus; ++ ++ /** ++ * Specifies whether to use the I2Cinterface for full speed PHY. This ++ * parameter is only applicable if PHY_TYPE is FS. ++ * 0 - No (default) ++ * 1 - Yes ++ */ ++ int32_t i2c_enable; ++ ++ int32_t ulpi_fs_ls; ++ ++ int32_t ts_dline; ++ ++ /** ++ * Specifies whether dedicated transmit FIFOs are ++ * enabled for non periodic IN endpoints in device mode ++ * 0 - No ++ * 1 - Yes ++ */ ++ int32_t en_multiple_tx_fifo; ++ ++ /** Number of 4-byte words in each of the Tx FIFOs in device ++ * mode when dynamic FIFO sizing is enabled. ++ * 4 to 768 (default 256) ++ */ ++ uint32_t dev_tx_fifo_size[MAX_TX_FIFOS]; ++ ++ /** Thresholding enable flag- ++ * bit 0 - enable non-ISO Tx thresholding ++ * bit 1 - enable ISO Tx thresholding ++ * bit 2 - enable Rx thresholding ++ */ ++ uint32_t thr_ctl; ++ ++ /** Thresholding length for Tx ++ * FIFOs in 32 bit DWORDs ++ */ ++ uint32_t tx_thr_length; ++ ++ /** Thresholding length for Rx ++ * FIFOs in 32 bit DWORDs ++ */ ++ uint32_t rx_thr_length; ++ ++ /** ++ * Specifies whether LPM (Link Power Management) support is enabled ++ */ ++ int32_t lpm_enable; ++ ++ /** Per Transfer Interrupt ++ * mode enable flag ++ * 1 - Enabled ++ * 0 - Disabled ++ */ ++ int32_t pti_enable; ++ ++ /** Multi Processor Interrupt ++ * mode enable flag ++ * 1 - Enabled ++ * 0 - Disabled ++ */ ++ int32_t mpi_enable; ++ ++ /** IS_USB Capability ++ * 1 - Enabled ++ * 0 - Disabled ++ */ ++ int32_t ic_usb_cap; ++ ++ /** AHB Threshold Ratio ++ * 2'b00 AHB Threshold = MAC Threshold ++ * 2'b01 AHB Threshold = 1/2 MAC Threshold ++ * 2'b10 AHB Threshold = 1/4 MAC Threshold ++ * 2'b11 AHB Threshold = 1/8 MAC Threshold ++ */ ++ int32_t ahb_thr_ratio; ++ ++} dwc_otg_core_params_t; ++ ++#ifdef DEBUG ++struct dwc_otg_core_if; ++typedef struct hc_xfer_info { ++ struct dwc_otg_core_if *core_if; ++ dwc_hc_t *hc; ++} hc_xfer_info_t; ++#endif ++/* ++ * Device States ++ */ ++typedef enum dwc_otg_lx_state { ++ /** On state */ ++ DWC_OTG_L0, ++ /** LPM sleep state*/ ++ DWC_OTG_L1, ++ /** USB suspend state*/ ++ DWC_OTG_L2, ++ /** Off state*/ ++ DWC_OTG_L3 ++} dwc_otg_lx_state_e; ++ ++/** ++ * The <code>dwc_otg_core_if</code> structure contains information needed to manage ++ * the DWC_otg controller acting in either host or device mode. It ++ * represents the programming view of the controller as a whole. ++ */ ++struct dwc_otg_core_if { ++ /** Parameters that define how the core should be configured.*/ ++ dwc_otg_core_params_t *core_params; ++ ++ /** Core Global registers starting at offset 000h. */ ++ dwc_otg_core_global_regs_t *core_global_regs; ++ ++ /** Device-specific information */ ++ dwc_otg_dev_if_t *dev_if; ++ /** Host-specific information */ ++ dwc_otg_host_if_t *host_if; ++ ++ /** Value from SNPSID register */ ++ uint32_t snpsid; ++ ++ /* ++ * Set to 1 if the core PHY interface bits in USBCFG have been ++ * initialized. ++ */ ++ uint8_t phy_init_done; ++ ++ /* ++ * SRP Success flag, set by srp success interrupt in FS I2C mode ++ */ ++ uint8_t srp_success; ++ uint8_t srp_timer_started; ++ ++ /* Common configuration information */ ++ /** Power and Clock Gating Control Register */ ++ volatile uint32_t *pcgcctl; ++#define DWC_OTG_PCGCCTL_OFFSET 0xE00 ++ ++ /** Push/pop addresses for endpoints or host channels.*/ ++ uint32_t *data_fifo[MAX_EPS_CHANNELS]; ++#define DWC_OTG_DATA_FIFO_OFFSET 0x1000 ++#define DWC_OTG_DATA_FIFO_SIZE 0x1000 ++ ++ /** Total RAM for FIFOs (Bytes) */ ++ uint16_t total_fifo_size; ++ /** Size of Rx FIFO (Bytes) */ ++ uint16_t rx_fifo_size; ++ /** Size of Non-periodic Tx FIFO (Bytes) */ ++ uint16_t nperio_tx_fifo_size; ++ ++ /** 1 if DMA is enabled, 0 otherwise. */ ++ uint8_t dma_enable; ++ ++ /** 1 if DMA descriptor is enabled, 0 otherwise. */ ++ uint8_t dma_desc_enable; ++ ++ /** 1 if PTI Enhancement mode is enabled, 0 otherwise. */ ++ uint8_t pti_enh_enable; ++ ++ /** 1 if MPI Enhancement mode is enabled, 0 otherwise. */ ++ uint8_t multiproc_int_enable; ++ ++ /** 1 if dedicated Tx FIFOs are enabled, 0 otherwise. */ ++ uint8_t en_multiple_tx_fifo; ++ ++ /** Set to 1 if multiple packets of a high-bandwidth transfer is in ++ * process of being queued */ ++ uint8_t queuing_high_bandwidth; ++ ++ /** Hardware Configuration -- stored here for convenience.*/ ++ hwcfg1_data_t hwcfg1; ++ hwcfg2_data_t hwcfg2; ++ hwcfg3_data_t hwcfg3; ++ hwcfg4_data_t hwcfg4; ++ ++ /** Host and Device Configuration -- stored here for convenience.*/ ++ hcfg_data_t hcfg; ++ dcfg_data_t dcfg; ++ ++ /** The operational State, during transations ++ * (a_host>>a_peripherial and b_device=>b_host) this may not ++ * match the core but allows the software to determine ++ * transitions. ++ */ ++ uint8_t op_state; ++ ++ /** ++ * Set to 1 if the HCD needs to be restarted on a session request ++ * interrupt. This is required if no connector ID status change has ++ * occurred since the HCD was last disconnected. ++ */ ++ uint8_t restart_hcd_on_session_req; ++ ++ /** HCD callbacks */ ++ /** A-Device is a_host */ ++#define A_HOST (1) ++ /** A-Device is a_suspend */ ++#define A_SUSPEND (2) ++ /** A-Device is a_peripherial */ ++#define A_PERIPHERAL (3) ++ /** B-Device is operating as a Peripheral. */ ++#define B_PERIPHERAL (4) ++ /** B-Device is operating as a Host. */ ++#define B_HOST (5) ++ ++ /** HCD callbacks */ ++ struct dwc_otg_cil_callbacks *hcd_cb; ++ /** PCD callbacks */ ++ struct dwc_otg_cil_callbacks *pcd_cb; ++ ++ /** Device mode Periodic Tx FIFO Mask */ ++ uint32_t p_tx_msk; ++ /** Device mode Periodic Tx FIFO Mask */ ++ uint32_t tx_msk; ++ ++ /** Workqueue object used for handling several interrupts */ ++ dwc_workq_t *wq_otg; ++ ++ /** Timer object used for handling "Wakeup Detected" Interrupt */ ++ dwc_timer_t *wkp_timer; ++ ++#ifdef DEBUG ++ uint32_t start_hcchar_val[MAX_EPS_CHANNELS]; ++ ++ hc_xfer_info_t hc_xfer_info[MAX_EPS_CHANNELS]; ++ dwc_timer_t *hc_xfer_timer[MAX_EPS_CHANNELS]; ++ ++ uint32_t hfnum_7_samples; ++ uint64_t hfnum_7_frrem_accum; ++ uint32_t hfnum_0_samples; ++ uint64_t hfnum_0_frrem_accum; ++ uint32_t hfnum_other_samples; ++ uint64_t hfnum_other_frrem_accum; ++#endif ++ ++#ifdef DWC_UTE_CFI ++ uint16_t pwron_rxfsiz; ++ uint16_t pwron_gnptxfsiz; ++ uint16_t pwron_txfsiz[15]; ++ ++ uint16_t init_rxfsiz; ++ uint16_t init_gnptxfsiz; ++ uint16_t init_txfsiz[15]; ++#endif ++ ++ /** Lx state of device */ ++ dwc_otg_lx_state_e lx_state; ++ ++}; ++ ++#ifdef DEBUG ++/* ++ * This function is called when transfer is timed out. ++ */ ++extern void hc_xfer_timeout(void *ptr); ++#endif ++ ++/* ++ * The following functions are functions for works ++ * using during handling some interrupts ++ */ ++extern void w_conn_id_status_change(void *p); ++ ++extern void w_wakeup_detected(void *p); ++ ++/* ++ * The following functions support initialization of the CIL driver component ++ * and the DWC_otg controller. ++ */ ++extern void dwc_otg_core_host_init(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_core_dev_init(dwc_otg_core_if_t * _core_if); ++ ++/** @name Device CIL Functions ++ * The following functions support managing the DWC_otg controller in device ++ * mode. ++ */ ++/**@{*/ ++extern void dwc_otg_wakeup(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_read_setup_packet(dwc_otg_core_if_t * _core_if, ++ uint32_t * _dest); ++extern uint32_t dwc_otg_get_frame_number(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_ep0_activate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep); ++extern void dwc_otg_ep_activate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep); ++extern void dwc_otg_ep_deactivate(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep); ++extern void dwc_otg_ep_start_transfer(dwc_otg_core_if_t * _core_if, ++ dwc_ep_t * _ep); ++extern void dwc_otg_ep_start_zl_transfer(dwc_otg_core_if_t * _core_if, ++ dwc_ep_t * _ep); ++extern void dwc_otg_ep0_start_transfer(dwc_otg_core_if_t * _core_if, ++ dwc_ep_t * _ep); ++extern void dwc_otg_ep0_continue_transfer(dwc_otg_core_if_t * _core_if, ++ dwc_ep_t * _ep); ++extern void dwc_otg_ep_write_packet(dwc_otg_core_if_t * _core_if, ++ dwc_ep_t * _ep, int _dma); ++extern void dwc_otg_ep_set_stall(dwc_otg_core_if_t * _core_if, dwc_ep_t * _ep); ++extern void dwc_otg_ep_clear_stall(dwc_otg_core_if_t * _core_if, ++ dwc_ep_t * _ep); ++extern void dwc_otg_enable_device_interrupts(dwc_otg_core_if_t * _core_if); ++ ++#ifdef DWC_EN_ISOC ++extern void dwc_otg_iso_ep_start_frm_transfer(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * ep); ++extern void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * ep); ++#endif /* DWC_EN_ISOC */ ++/**@}*/ ++ ++/** @name Host CIL Functions ++ * The following functions support managing the DWC_otg controller in host ++ * mode. ++ */ ++/**@{*/ ++extern void dwc_otg_hc_init(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc); ++extern void dwc_otg_hc_halt(dwc_otg_core_if_t * _core_if, ++ dwc_hc_t * _hc, dwc_otg_halt_status_e _halt_status); ++extern void dwc_otg_hc_cleanup(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc); ++extern void dwc_otg_hc_start_transfer(dwc_otg_core_if_t * _core_if, ++ dwc_hc_t * _hc); ++extern int dwc_otg_hc_continue_transfer(dwc_otg_core_if_t * _core_if, ++ dwc_hc_t * _hc); ++extern void dwc_otg_hc_do_ping(dwc_otg_core_if_t * _core_if, dwc_hc_t * _hc); ++extern void dwc_otg_hc_write_packet(dwc_otg_core_if_t * _core_if, ++ dwc_hc_t * _hc); ++extern void dwc_otg_enable_host_interrupts(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_disable_host_interrupts(dwc_otg_core_if_t * _core_if); ++ ++extern void dwc_otg_hc_start_transfer_ddma(dwc_otg_core_if_t * core_if, dwc_hc_t * hc); ++ ++/* Macro used to clear one channel interrupt */ ++#define clear_hc_int(_hc_regs_, _intr_) \ ++do { \ ++ hcint_data_t hcint_clear = {.d32 = 0}; \ ++ hcint_clear.b._intr_ = 1; \ ++ dwc_write_reg32(&(_hc_regs_)->hcint, hcint_clear.d32); \ ++} while (0) ++ ++/* ++ * Macro used to disable one channel interrupt. Channel interrupts are ++ * disabled when the channel is halted or released by the interrupt handler. ++ * There is no need to handle further interrupts of that type until the ++ * channel is re-assigned. In fact, subsequent handling may cause crashes ++ * because the channel structures are cleaned up when the channel is released. ++ */ ++#define disable_hc_int(_hc_regs_, _intr_) \ ++do { \ ++ hcintmsk_data_t hcintmsk = {.d32 = 0}; \ ++ hcintmsk.b._intr_ = 1; \ ++ dwc_modify_reg32(&(_hc_regs_)->hcintmsk, hcintmsk.d32, 0); \ ++} while (0) ++ ++/** ++ * This function Reads HPRT0 in preparation to modify. It keeps the ++ * WC bits 0 so that if they are read as 1, they won't clear when you ++ * write it back ++ */ ++static inline uint32_t dwc_otg_read_hprt0(dwc_otg_core_if_t * _core_if) ++{ ++ hprt0_data_t hprt0; ++ hprt0.d32 = dwc_read_reg32(_core_if->host_if->hprt0); ++ hprt0.b.prtena = 0; ++ hprt0.b.prtconndet = 0; ++ hprt0.b.prtenchng = 0; ++ hprt0.b.prtovrcurrchng = 0; ++ return hprt0.d32; ++} ++ ++/**@}*/ ++ ++/** @name Common CIL Functions ++ * The following functions support managing the DWC_otg controller in either ++ * device or host mode. ++ */ ++/**@{*/ ++ ++extern void dwc_otg_read_packet(dwc_otg_core_if_t * core_if, ++ uint8_t * dest, uint16_t bytes); ++ ++extern void dwc_otg_flush_tx_fifo(dwc_otg_core_if_t * _core_if, const int _num); ++extern void dwc_otg_flush_rx_fifo(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_core_reset(dwc_otg_core_if_t * _core_if); ++ ++/** ++ * This function returns the Core Interrupt register. ++ */ ++static inline uint32_t dwc_otg_read_core_intr(dwc_otg_core_if_t * core_if) ++{ ++ return (dwc_read_reg32(&core_if->core_global_regs->gintsts) & ++ dwc_read_reg32(&core_if->core_global_regs->gintmsk)); ++} ++ ++/** ++ * This function returns the OTG Interrupt register. ++ */ ++static inline uint32_t dwc_otg_read_otg_intr(dwc_otg_core_if_t * core_if) ++{ ++ return (dwc_read_reg32(&core_if->core_global_regs->gotgint)); ++} ++ ++/** ++ * This function reads the Device All Endpoints Interrupt register and ++ * returns the IN endpoint interrupt bits. ++ */ ++static inline uint32_t dwc_otg_read_dev_all_in_ep_intr(dwc_otg_core_if_t * ++ core_if) ++{ ++ ++ uint32_t v; ++ ++ if (core_if->multiproc_int_enable) { ++ v = dwc_read_reg32(&core_if->dev_if->dev_global_regs-> ++ deachint) & dwc_read_reg32(&core_if->dev_if-> ++ dev_global_regs-> ++ deachintmsk); ++ } else { ++ v = dwc_read_reg32(&core_if->dev_if->dev_global_regs->daint) & ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs->daintmsk); ++ } ++ return (v & 0xffff); ++} ++ ++/** ++ * This function reads the Device All Endpoints Interrupt register and ++ * returns the OUT endpoint interrupt bits. ++ */ ++static inline uint32_t dwc_otg_read_dev_all_out_ep_intr(dwc_otg_core_if_t * ++ core_if) ++{ ++ uint32_t v; ++ ++ if (core_if->multiproc_int_enable) { ++ v = dwc_read_reg32(&core_if->dev_if->dev_global_regs-> ++ deachint) & dwc_read_reg32(&core_if->dev_if-> ++ dev_global_regs-> ++ deachintmsk); ++ } else { ++ v = dwc_read_reg32(&core_if->dev_if->dev_global_regs->daint) & ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs->daintmsk); ++ } ++ ++ return ((v & 0xffff0000) >> 16); ++} ++ ++/** ++ * This function returns the Device IN EP Interrupt register ++ */ ++static inline uint32_t dwc_otg_read_dev_in_ep_intr(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * ep) ++{ ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ uint32_t v, msk, emp; ++ ++ if (core_if->multiproc_int_enable) { ++ msk = ++ dwc_read_reg32(&dev_if->dev_global_regs-> ++ diepeachintmsk[ep->num]); ++ emp = ++ dwc_read_reg32(&dev_if->dev_global_regs-> ++ dtknqr4_fifoemptymsk); ++ msk |= ((emp >> ep->num) & 0x1) << 7; ++ v = dwc_read_reg32(&dev_if->in_ep_regs[ep->num]->diepint) & msk; ++ } else { ++ msk = dwc_read_reg32(&dev_if->dev_global_regs->diepmsk); ++ emp = ++ dwc_read_reg32(&dev_if->dev_global_regs-> ++ dtknqr4_fifoemptymsk); ++ msk |= ((emp >> ep->num) & 0x1) << 7; ++ v = dwc_read_reg32(&dev_if->in_ep_regs[ep->num]->diepint) & msk; ++ } ++ ++ return v; ++} ++ ++/** ++ * This function returns the Device OUT EP Interrupt register ++ */ ++static inline uint32_t dwc_otg_read_dev_out_ep_intr(dwc_otg_core_if_t * ++ _core_if, dwc_ep_t * _ep) ++{ ++ dwc_otg_dev_if_t *dev_if = _core_if->dev_if; ++ uint32_t v; ++ doepmsk_data_t msk = {.d32 = 0 }; ++ ++ if (_core_if->multiproc_int_enable) { ++ msk.d32 = ++ dwc_read_reg32(&dev_if->dev_global_regs-> ++ doepeachintmsk[_ep->num]); ++ if (_core_if->pti_enh_enable) { ++ msk.b.pktdrpsts = 1; ++ } ++ v = dwc_read_reg32(&dev_if->out_ep_regs[_ep->num]-> ++ doepint) & msk.d32; ++ } else { ++ msk.d32 = dwc_read_reg32(&dev_if->dev_global_regs->doepmsk); ++ if (_core_if->pti_enh_enable) { ++ msk.b.pktdrpsts = 1; ++ } ++ v = dwc_read_reg32(&dev_if->out_ep_regs[_ep->num]-> ++ doepint) & msk.d32; ++ } ++ return v; ++} ++ ++/** ++ * This function returns the Host All Channel Interrupt register ++ */ ++static inline uint32_t dwc_otg_read_host_all_channels_intr(dwc_otg_core_if_t * ++ _core_if) ++{ ++ return (dwc_read_reg32(&_core_if->host_if->host_global_regs->haint)); ++} ++ ++static inline uint32_t dwc_otg_read_host_channel_intr(dwc_otg_core_if_t * ++ _core_if, dwc_hc_t * _hc) ++{ ++ return (dwc_read_reg32 ++ (&_core_if->host_if->hc_regs[_hc->hc_num]->hcint)); ++} ++ ++/** ++ * This function returns the mode of the operation, host or device. ++ * ++ * @return 0 - Device Mode, 1 - Host Mode ++ */ ++static inline uint32_t dwc_otg_mode(dwc_otg_core_if_t * _core_if) ++{ ++ return (dwc_read_reg32(&_core_if->core_global_regs->gintsts) & 0x1); ++} ++ ++/**@}*/ ++ ++/** ++ * DWC_otg CIL callback structure. This structure allows the HCD and ++ * PCD to register functions used for starting and stopping the PCD ++ * and HCD for role change on for a DRD. ++ */ ++typedef struct dwc_otg_cil_callbacks { ++ /** Start function for role change */ ++ int (*start) (void *_p); ++ /** Stop Function for role change */ ++ int (*stop) (void *_p); ++ /** Disconnect Function for role change */ ++ int (*disconnect) (void *_p); ++ /** Resume/Remote wakeup Function */ ++ int (*resume_wakeup) (void *_p); ++ /** Suspend function */ ++ int (*suspend) (void *_p); ++ /** Session Start (SRP) */ ++ int (*session_start) (void *_p); ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ /** Sleep (switch to L0 state) */ ++ int (*sleep) (void *_p); ++#endif ++ /** Pointer passed to start() and stop() */ ++ void *p; ++} dwc_otg_cil_callbacks_t; ++ ++extern void dwc_otg_cil_register_pcd_callbacks(dwc_otg_core_if_t * _core_if, ++ dwc_otg_cil_callbacks_t * _cb, ++ void *_p); ++extern void dwc_otg_cil_register_hcd_callbacks(dwc_otg_core_if_t * _core_if, ++ dwc_otg_cil_callbacks_t * _cb, ++ void *_p); ++ ++#endif +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_cil_intr.c +@@ -0,0 +1,846 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_cil_intr.c $ ++ * $Revision: #15 $ ++ * $Date: 2009/04/15 $ ++ * $Change: 1234129 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * ++ * The Core Interface Layer provides basic services for accessing and ++ * managing the DWC_otg hardware. These services are used by both the ++ * Host Controller Driver and the Peripheral Controller Driver. ++ * ++ * This file contains the Common Interrupt handlers. ++ */ ++#include "dwc_os.h" ++#include "dwc_otg_regs.h" ++#include "dwc_otg_cil.h" ++ ++#ifdef DEBUG ++inline const char *op_state_str(dwc_otg_core_if_t * core_if) ++{ ++ return (core_if->op_state == A_HOST ? "a_host" : ++ (core_if->op_state == A_SUSPEND ? "a_suspend" : ++ (core_if->op_state == A_PERIPHERAL ? "a_peripheral" : ++ (core_if->op_state == B_PERIPHERAL ? "b_peripheral" : ++ (core_if->op_state == B_HOST ? "b_host" : "unknown"))))); ++} ++#endif ++ ++/** This function will log a debug message ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_mode_mismatch_intr(dwc_otg_core_if_t * core_if) ++{ ++ gintsts_data_t gintsts; ++ DWC_WARN("Mode Mismatch Interrupt: currently in %s mode\n", ++ dwc_otg_mode(core_if) ? "Host" : "Device"); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.modemismatch = 1; ++ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++ ++/** Start the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_start(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->start) { ++ core_if->hcd_cb->start(core_if->hcd_cb->p); ++ } ++} ++ ++/** Stop the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_stop(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->stop) { ++ core_if->hcd_cb->stop(core_if->hcd_cb->p); ++ } ++} ++ ++/** Disconnect the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_disconnect(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->disconnect) { ++ core_if->hcd_cb->disconnect(core_if->hcd_cb->p); ++ } ++} ++ ++/** Inform the HCD the a New Session has begun. Helper function for ++ * using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_session_start(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->session_start) { ++ core_if->hcd_cb->session_start(core_if->hcd_cb->p); ++ } ++} ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++/** ++ * Inform the HCD about LPM sleep. ++ * Helper function for using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_sleep(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->sleep) { ++ core_if->hcd_cb->sleep(core_if->hcd_cb->p); ++ } ++} ++#endif ++ ++/** Resume the HCD. Helper function for using the HCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void hcd_resume(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->hcd_cb && core_if->hcd_cb->resume_wakeup) { ++ core_if->hcd_cb->resume_wakeup(core_if->hcd_cb->p); ++ } ++} ++ ++/** Start the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void pcd_start(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->pcd_cb && core_if->pcd_cb->start) { ++ core_if->pcd_cb->start(core_if->pcd_cb->p); ++ } ++} ++ ++/** Stop the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void pcd_stop(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->pcd_cb && core_if->pcd_cb->stop) { ++ core_if->pcd_cb->stop(core_if->pcd_cb->p); ++ } ++} ++ ++/** Suspend the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void pcd_suspend(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->pcd_cb && core_if->pcd_cb->suspend) { ++ core_if->pcd_cb->suspend(core_if->pcd_cb->p); ++ } ++} ++ ++/** Resume the PCD. Helper function for using the PCD callbacks. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static inline void pcd_resume(dwc_otg_core_if_t * core_if) ++{ ++ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { ++ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb->p); ++ } ++} ++ ++/** ++ * This function handles the OTG Interrupts. It reads the OTG ++ * Interrupt Register (GOTGINT) to determine what interrupt has ++ * occurred. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_otg_intr(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ gotgint_data_t gotgint; ++ gotgctl_data_t gotgctl; ++ gintmsk_data_t gintmsk; ++ ++ gotgint.d32 = dwc_read_reg32(&global_regs->gotgint); ++ gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl); ++ DWC_DEBUGPL(DBG_CIL, "++OTG Interrupt gotgint=%0x [%s]\n", gotgint.d32, ++ op_state_str(core_if)); ++ ++ if (gotgint.b.sesenddet) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "Session End Detected++ (%s)\n", ++ op_state_str(core_if)); ++ gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl); ++ ++ if (core_if->op_state == B_HOST) { ++ pcd_start(core_if); ++ core_if->op_state = B_PERIPHERAL; ++ } else { ++ /* If not B_HOST and Device HNP still set. HNP ++ * Did not succeed!*/ ++ if (gotgctl.b.devhnpen) { ++ DWC_DEBUGPL(DBG_ANY, "Session End Detected\n"); ++ __DWC_ERROR("Device Not Connected/Responding!\n"); ++ } ++ ++ /* If Session End Detected the B-Cable has ++ * been disconnected. */ ++ /* Reset PCD and Gadget driver to a ++ * clean state. */ ++ core_if->lx_state = DWC_OTG_L0; ++ pcd_stop(core_if); ++ } ++ gotgctl.d32 = 0; ++ gotgctl.b.devhnpen = 1; ++ dwc_modify_reg32(&global_regs->gotgctl, gotgctl.d32, 0); ++ } ++ if (gotgint.b.sesreqsucstschng) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "Session Reqeust Success Status Change++\n"); ++ gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl); ++ if (gotgctl.b.sesreqscs) { ++ if ((core_if->core_params->phy_type == ++ DWC_PHY_TYPE_PARAM_FS) && (core_if->core_params->i2c_enable)) { ++ core_if->srp_success = 1; ++ } else { ++ pcd_resume(core_if); ++ /* Clear Session Request */ ++ gotgctl.d32 = 0; ++ gotgctl.b.sesreq = 1; ++ dwc_modify_reg32(&global_regs->gotgctl, ++ gotgctl.d32, 0); ++ } ++ } ++ } ++ if (gotgint.b.hstnegsucstschng) { ++ /* Print statements during the HNP interrupt handling ++ * can cause it to fail.*/ ++ gotgctl.d32 = dwc_read_reg32(&global_regs->gotgctl); ++ if (gotgctl.b.hstnegscs) { ++ if (dwc_otg_is_host_mode(core_if)) { ++ core_if->op_state = B_HOST; ++ /* ++ * Need to disable SOF interrupt immediately. ++ * When switching from device to host, the PCD ++ * interrupt handler won't handle the ++ * interrupt if host mode is already set. The ++ * HCD interrupt handler won't get called if ++ * the HCD state is HALT. This means that the ++ * interrupt does not get handled and Linux ++ * complains loudly. ++ */ ++ gintmsk.d32 = 0; ++ gintmsk.b.sofintr = 1; ++ dwc_modify_reg32(&global_regs->gintmsk, ++ gintmsk.d32, 0); ++ pcd_stop(core_if); ++ /* ++ * Initialize the Core for Host mode. ++ */ ++ hcd_start(core_if); ++ core_if->op_state = B_HOST; ++ } ++ } else { ++ gotgctl.d32 = 0; ++ gotgctl.b.hnpreq = 1; ++ gotgctl.b.devhnpen = 1; ++ dwc_modify_reg32(&global_regs->gotgctl, gotgctl.d32, 0); ++ DWC_DEBUGPL(DBG_ANY, "HNP Failed\n"); ++ __DWC_ERROR("Device Not Connected/Responding\n"); ++ } ++ } ++ if (gotgint.b.hstnegdet) { ++ /* The disconnect interrupt is set at the same time as ++ * Host Negotiation Detected. During the mode ++ * switch all interrupts are cleared so the disconnect ++ * interrupt handler will not get executed. ++ */ ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "Host Negotiation Detected++ (%s)\n", ++ (dwc_otg_is_host_mode(core_if) ? "Host" : ++ "Device")); ++ if (dwc_otg_is_device_mode(core_if)) { ++ DWC_DEBUGPL(DBG_ANY, "a_suspend->a_peripheral (%d)\n", ++ core_if->op_state); ++ hcd_disconnect(core_if); ++ pcd_start(core_if); ++ core_if->op_state = A_PERIPHERAL; ++ } else { ++ /* ++ * Need to disable SOF interrupt immediately. When ++ * switching from device to host, the PCD interrupt ++ * handler won't handle the interrupt if host mode is ++ * already set. The HCD interrupt handler won't get ++ * called if the HCD state is HALT. This means that ++ * the interrupt does not get handled and Linux ++ * complains loudly. ++ */ ++ gintmsk.d32 = 0; ++ gintmsk.b.sofintr = 1; ++ dwc_modify_reg32(&global_regs->gintmsk, gintmsk.d32, 0); ++ pcd_stop(core_if); ++ hcd_start(core_if); ++ core_if->op_state = A_HOST; ++ } ++ } ++ if (gotgint.b.adevtoutchng) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " ++ "A-Device Timeout Change++\n"); ++ } ++ if (gotgint.b.debdone) { ++ DWC_DEBUGPL(DBG_ANY, " ++OTG Interrupt: " "Debounce Done++\n"); ++ } ++ ++ /* Clear GOTGINT */ ++ dwc_write_reg32(&core_if->core_global_regs->gotgint, gotgint.d32); ++ ++ return 1; ++} ++ ++void w_conn_id_status_change(void *p) ++{ ++ dwc_otg_core_if_t *core_if = p; ++ uint32_t count = 0; ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ ++ gotgctl.d32 = dwc_read_reg32(&core_if->core_global_regs->gotgctl); ++ DWC_DEBUGPL(DBG_CIL, "gotgctl=%0x\n", gotgctl.d32); ++ DWC_DEBUGPL(DBG_CIL, "gotgctl.b.conidsts=%d\n", gotgctl.b.conidsts); ++ ++ /* B-Device connector (Device Mode) */ ++ if (gotgctl.b.conidsts) { ++ /* Wait for switch to device mode. */ ++ while (!dwc_otg_is_device_mode(core_if)) { ++ DWC_PRINTF("Waiting for Peripheral Mode, Mode=%s\n", ++ (dwc_otg_is_host_mode(core_if) ? "Host" : ++ "Peripheral")); ++ dwc_mdelay(100); ++ if (++count > 10000) ++ break; ++ } ++ DWC_ASSERT(++count < 10000, ++ "Connection id status change timed out"); ++ core_if->op_state = B_PERIPHERAL; ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ pcd_start(core_if); ++ } else { ++ /* A-Device connector (Host Mode) */ ++ while (!dwc_otg_is_host_mode(core_if)) { ++ DWC_PRINTF("Waiting for Host Mode, Mode=%s\n", ++ (dwc_otg_is_host_mode(core_if) ? "Host" : ++ "Peripheral")); ++ dwc_mdelay(100); ++ if (++count > 10000) ++ break; ++ } ++ DWC_ASSERT(++count < 10000, ++ "Connection id status change timed out"); ++ core_if->op_state = A_HOST; ++ /* ++ * Initialize the Core for Host mode. ++ */ ++ dwc_otg_core_init(core_if); ++ dwc_otg_enable_global_interrupts(core_if); ++ hcd_start(core_if); ++ } ++} ++ ++/** ++ * This function handles the Connector ID Status Change Interrupt. It ++ * reads the OTG Interrupt Register (GOTCTL) to determine whether this ++ * is a Device to Host Mode transition or a Host Mode to Device ++ * Transition. ++ * ++ * This only occurs when the cable is connected/removed from the PHY ++ * connector. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_conn_id_status_change_intr(dwc_otg_core_if_t * core_if) ++{ ++ ++ /* ++ * Need to disable SOF interrupt immediately. If switching from device ++ * to host, the PCD interrupt handler won't handle the interrupt if ++ * host mode is already set. The HCD interrupt handler won't get ++ * called if the HCD state is HALT. This means that the interrupt does ++ * not get handled and Linux complains loudly. ++ */ ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ gintsts_data_t gintsts = {.d32 = 0 }; ++ ++ gintmsk.b.sofintr = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0); ++ ++ DWC_DEBUGPL(DBG_CIL, ++ " ++Connector ID Status Change Interrupt++ (%s)\n", ++ (dwc_otg_is_host_mode(core_if) ? "Host" : "Device")); ++ ++ /* ++ * Need to schedule a work, as there are possible DELAY function calls ++ */ ++ DWC_WORKQ_SCHEDULE(core_if->wq_otg, w_conn_id_status_change, ++ core_if, "connection id status change"); ++ ++ /* Set flag and clear interrupt */ ++ gintsts.b.conidstschng = 1; ++ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that a device is initiating the Session ++ * Request Protocol to request the host to turn on bus power so a new ++ * session can begin. The handler responds by turning on bus power. If ++ * the DWC_otg controller is in low power mode, the handler brings the ++ * controller out of low power mode before turning on bus power. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++int32_t dwc_otg_handle_session_req_intr(dwc_otg_core_if_t * core_if) ++{ ++ hprt0_data_t hprt0; ++ gintsts_data_t gintsts; ++ ++#ifndef DWC_HOST_ONLY ++ DWC_DEBUGPL(DBG_ANY, "++Session Request Interrupt++\n"); ++ ++ if (dwc_otg_is_device_mode(core_if)) { ++ DWC_PRINTF("SRP: Device mode\n"); ++ } else { ++ DWC_PRINTF("SRP: Host mode\n"); ++ ++ /* Turn on the port power bit. */ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtpwr = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ ++ /* Start the Connection timer. So a message can be displayed ++ * if connect does not occur within 10 seconds. */ ++ hcd_session_start(core_if); ++ } ++#endif ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.sessreqintr = 1; ++ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++void w_wakeup_detected(void *p) ++{ ++ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) p; ++ /* ++ * Clear the Resume after 70ms. (Need 20 ms minimum. Use 70 ms ++ * so that OPT tests pass with all PHYs). ++ */ ++ hprt0_data_t hprt0 = {.d32 = 0 }; ++#if 0 ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ /* Restart the Phy Clock */ ++ pcgcctl.b.stoppclk = 1; ++ dwc_modify_reg32(core_if->pcgcctl, pcgcctl.d32, 0); ++ dwc_udelay(10); ++#endif //0 ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ DWC_DEBUGPL(DBG_ANY, "Resume: HPRT0=%0x\n", hprt0.d32); ++// dwc_mdelay(70); ++ hprt0.b.prtres = 0; /* Resume */ ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ DWC_DEBUGPL(DBG_ANY, "Clear Resume: HPRT0=%0x\n", ++ dwc_read_reg32(core_if->host_if->hprt0)); ++ ++ hcd_resume(core_if); ++ ++ /** Change to L0 state*/ ++ core_if->lx_state = DWC_OTG_L0; ++ ++} ++ ++/** ++ * This interrupt indicates that the DWC_otg controller has detected a ++ * resume or remote wakeup sequence. If the DWC_otg controller is in ++ * low power mode, the handler must brings the controller out of low ++ * power mode. The controller automatically begins resume ++ * signaling. The handler schedules a time to stop resume signaling. ++ */ ++int32_t dwc_otg_handle_wakeup_detected_intr(dwc_otg_core_if_t * core_if) ++{ ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_ANY, ++ "++Resume and Remote Wakeup Detected Interrupt++\n"); ++ ++ DWC_PRINTF("%s lxstate = %d\n", __func__, core_if->lx_state); ++ ++ if (dwc_otg_is_device_mode(core_if)) { ++ dctl_data_t dctl = {.d32 = 0 }; ++ DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs-> ++ dsts)); ++ if (core_if->lx_state == DWC_OTG_L2) { ++#ifdef PARTIAL_POWER_DOWN ++ if (core_if->hwcfg4.b.power_optimiz) { ++ pcgcctl_data_t power = {.d32 = 0 }; ++ ++ power.d32 = dwc_read_reg32(core_if->pcgcctl); ++ DWC_DEBUGPL(DBG_CIL, "PCGCCTL=%0x\n", ++ power.d32); ++ ++ power.b.stoppclk = 0; ++ dwc_write_reg32(core_if->pcgcctl, power.d32); ++ ++ power.b.pwrclmp = 0; ++ dwc_write_reg32(core_if->pcgcctl, power.d32); ++ ++ power.b.rstpdwnmodule = 0; ++ dwc_write_reg32(core_if->pcgcctl, power.d32); ++ } ++#endif ++ /* Clear the Remote Wakeup Signalling */ ++ dctl.b.rmtwkupsig = 1; ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs-> ++ dctl, dctl.d32, 0); ++ ++ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { ++ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb-> ++ p); ++ } ++ } else { ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = ++ dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ lpmcfg.b.hird_thres &= (~(1 << 4)); ++ dwc_write_reg32(&core_if->core_global_regs->glpmcfg, ++ lpmcfg.d32); ++ } ++ /** Change to L0 state*/ ++ core_if->lx_state = DWC_OTG_L0; ++ } else { ++ if (core_if->lx_state != DWC_OTG_L1) { ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ ++ /* Restart the Phy Clock */ ++ pcgcctl.b.stoppclk = 1; ++ dwc_modify_reg32(core_if->pcgcctl, pcgcctl.d32, 0); ++ ++ DWC_TIMER_SCHEDULE(core_if->wkp_timer, 71); ++ } else { ++ /** Change to L0 state*/ ++ core_if->lx_state = DWC_OTG_L0; ++ } ++ } ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.wkupintr = 1; ++ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that a device has been disconnected from ++ * the root port. ++ */ ++int32_t dwc_otg_handle_disconnect_intr(dwc_otg_core_if_t * core_if) ++{ ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_ANY, "++Disconnect Detected Interrupt++ (%s) %s\n", ++ (dwc_otg_is_host_mode(core_if) ? "Host" : "Device"), ++ op_state_str(core_if)); ++ ++/** @todo Consolidate this if statement. */ ++#ifndef DWC_HOST_ONLY ++ if (core_if->op_state == B_HOST) { ++ /* If in device mode Disconnect and stop the HCD, then ++ * start the PCD. */ ++ hcd_disconnect(core_if); ++ pcd_start(core_if); ++ core_if->op_state = B_PERIPHERAL; ++ } else if (dwc_otg_is_device_mode(core_if)) { ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ gotgctl.d32 = ++ dwc_read_reg32(&core_if->core_global_regs->gotgctl); ++ if (gotgctl.b.hstsethnpen == 1) { ++ /* Do nothing, if HNP in process the OTG ++ * interrupt "Host Negotiation Detected" ++ * interrupt will do the mode switch. ++ */ ++ } else if (gotgctl.b.devhnpen == 0) { ++ /* If in device mode Disconnect and stop the HCD, then ++ * start the PCD. */ ++ hcd_disconnect(core_if); ++ pcd_start(core_if); ++ core_if->op_state = B_PERIPHERAL; ++ } else { ++ DWC_DEBUGPL(DBG_ANY, "!a_peripheral && !devhnpen\n"); ++ } ++ } else { ++ if (core_if->op_state == A_HOST) { ++ /* A-Cable still connected but device disconnected. */ ++ hcd_disconnect(core_if); ++ } ++ } ++#endif ++ /* Change to L3(OFF) state */ ++ core_if->lx_state = DWC_OTG_L3; ++ ++ gintsts.d32 = 0; ++ gintsts.b.disconnect = 1; ++ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that SUSPEND state has been detected on ++ * the USB. ++ * ++ * For HNP the USB Suspend interrupt signals the change from ++ * "a_peripheral" to "a_host". ++ * ++ * When power management is enabled the core will be put in low power ++ * mode. ++ */ ++int32_t dwc_otg_handle_usb_suspend_intr(dwc_otg_core_if_t * core_if) ++{ ++ dsts_data_t dsts; ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_ANY, "USB SUSPEND\n"); ++ ++ if (dwc_otg_is_device_mode(core_if)) { ++ /* Check the Device status register to determine if the Suspend ++ * state is active. */ ++ dsts.d32 = ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ DWC_DEBUGPL(DBG_PCD, "DSTS=0x%0x\n", dsts.d32); ++ DWC_DEBUGPL(DBG_PCD, "DSTS.Suspend Status=%d " ++ "HWCFG4.power Optimize=%d\n", ++ dsts.b.suspsts, core_if->hwcfg4.b.power_optimiz); ++ ++#ifdef PARTIAL_POWER_DOWN ++/** @todo Add a module parameter for power management. */ ++ ++ if (dsts.b.suspsts && core_if->hwcfg4.b.power_optimiz) { ++ pcgcctl_data_t power = {.d32 = 0 }; ++ DWC_DEBUGPL(DBG_CIL, "suspend\n"); ++ ++ power.b.pwrclmp = 1; ++ dwc_write_reg32(core_if->pcgcctl, power.d32); ++ ++ power.b.rstpdwnmodule = 1; ++ dwc_modify_reg32(core_if->pcgcctl, 0, power.d32); ++ ++ power.b.stoppclk = 1; ++ dwc_modify_reg32(core_if->pcgcctl, 0, power.d32); ++ ++ } else { ++ DWC_DEBUGPL(DBG_ANY, "disconnect?\n"); ++ } ++#endif ++ /* PCD callback for suspend. */ ++ pcd_suspend(core_if); ++ } else { ++ if (core_if->op_state == A_PERIPHERAL) { ++ DWC_DEBUGPL(DBG_ANY, "a_peripheral->a_host\n"); ++ /* Clear the a_peripheral flag, back to a_host. */ ++ pcd_stop(core_if); ++ hcd_start(core_if); ++ core_if->op_state = A_HOST; ++ } ++ } ++ ++ /* Change to L2(suspend) state */ ++ core_if->lx_state = DWC_OTG_L2; ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.usbsuspend = 1; ++ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++/** ++ * This function hadles LPM transaction received interrupt. ++ */ ++static int32_t dwc_otg_handle_lpm_intr(dwc_otg_core_if_t * core_if) ++{ ++ glpmcfg_data_t lpmcfg; ++ gintsts_data_t gintsts; ++ ++ if (!core_if->core_params->lpm_enable) { ++ DWC_PRINTF("Unexpected LPM interrupt\n"); ++ } ++ ++ lpmcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ DWC_PRINTF("LPM config register = 0x%08x\n", lpmcfg.d32); ++ ++ if (dwc_otg_is_host_mode(core_if)) { ++ hcd_sleep(core_if); ++ } else { ++ lpmcfg.b.hird_thres |= (1 << 4); ++ dwc_write_reg32(&core_if->core_global_regs->glpmcfg, ++ lpmcfg.d32); ++ } ++ ++ /* Examine prt_sleep_sts after TL1TokenTetry period max (10 us) */ ++ dwc_udelay(10); ++ lpmcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ if (lpmcfg.b.prt_sleep_sts) { ++ /* Save the current state */ ++ core_if->lx_state = DWC_OTG_L1; ++ } ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.lpmtranrcvd = 1; ++ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ return 1; ++} ++#endif /* CONFIG_USB_DWC_OTG_LPM */ ++ ++/** ++ * This function returns the Core Interrupt register. ++ */ ++static inline uint32_t dwc_otg_read_common_intr(dwc_otg_core_if_t * core_if) ++{ ++ gintsts_data_t gintsts; ++ gintmsk_data_t gintmsk; ++ gintmsk_data_t gintmsk_common = {.d32 = 0 }; ++ gintmsk_common.b.wkupintr = 1; ++ gintmsk_common.b.sessreqintr = 1; ++ gintmsk_common.b.conidstschng = 1; ++ gintmsk_common.b.otgintr = 1; ++ gintmsk_common.b.modemismatch = 1; ++ gintmsk_common.b.disconnect = 1; ++ gintmsk_common.b.usbsuspend = 1; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ gintmsk_common.b.lpmtranrcvd = 1; ++#endif ++ /** @todo: The port interrupt occurs while in device ++ * mode. Added code to CIL to clear the interrupt for now! ++ */ ++ gintmsk_common.b.portintr = 1; ++ ++ gintsts.d32 = dwc_read_reg32(&core_if->core_global_regs->gintsts); ++ gintmsk.d32 = dwc_read_reg32(&core_if->core_global_regs->gintmsk); ++#ifdef DEBUG ++ /* if any common interrupts set */ ++ if (gintsts.d32 & gintmsk_common.d32) { ++ DWC_DEBUGPL(DBG_ANY, "gintsts=%08x gintmsk=%08x\n", ++ gintsts.d32, gintmsk.d32); ++ } ++#endif ++ ++ return ((gintsts.d32 & gintmsk.d32) & gintmsk_common.d32); ++ ++} ++ ++/** ++ * Common interrupt handler. ++ * ++ * The common interrupts are those that occur in both Host and Device mode. ++ * This handler handles the following interrupts: ++ * - Mode Mismatch Interrupt ++ * - Disconnect Interrupt ++ * - OTG Interrupt ++ * - Connector ID Status Change Interrupt ++ * - Session Request Interrupt. ++ * - Resume / Remote Wakeup Detected Interrupt. ++ * - LPM Transaction Received Interrutp ++ * ++ */ ++int32_t dwc_otg_handle_common_intr(dwc_otg_core_if_t * core_if) ++{ ++ int retval = 0; ++ gintsts_data_t gintsts; ++ ++ gintsts.d32 = dwc_otg_read_common_intr(core_if); ++ ++ if (gintsts.b.modemismatch) { ++ retval |= dwc_otg_handle_mode_mismatch_intr(core_if); ++ } ++ if (gintsts.b.otgintr) { ++ retval |= dwc_otg_handle_otg_intr(core_if); ++ } ++ if (gintsts.b.conidstschng) { ++ retval |= dwc_otg_handle_conn_id_status_change_intr(core_if); ++ } ++ if (gintsts.b.disconnect) { ++ retval |= dwc_otg_handle_disconnect_intr(core_if); ++ } ++ if (gintsts.b.sessreqintr) { ++ retval |= dwc_otg_handle_session_req_intr(core_if); ++ } ++ if (gintsts.b.wkupintr) { ++ retval |= dwc_otg_handle_wakeup_detected_intr(core_if); ++ } ++ if (gintsts.b.usbsuspend) { ++ retval |= dwc_otg_handle_usb_suspend_intr(core_if); ++ } ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ if (gintsts.b.lpmtranrcvd) { ++ retval |= dwc_otg_handle_lpm_intr(core_if); ++ } ++#endif ++ ++ if (gintsts.b.portintr && dwc_otg_is_device_mode(core_if)) { ++ /* The port interrupt occurs while in device mode with HPRT0 ++ * Port Enable/Disable. ++ */ ++ gintsts.d32 = 0; ++ gintsts.b.portintr = 1; ++ dwc_write_reg32(&core_if->core_global_regs->gintsts, ++ gintsts.d32); ++ retval |= 1; ++ ++ } ++ return retval; ++} +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_core_if.h +@@ -0,0 +1,641 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_core_if.h $ ++ * $Revision: #4 $ ++ * $Date: 2008/12/18 $ ++ * $Change: 1155299 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#if !defined(__DWC_CORE_IF_H__) ++#define __DWC_CORE_IF_H__ ++ ++#include "dwc_os.h" ++ ++/** @file ++ * This file defines DWC_OTG Core API ++ */ ++ ++struct dwc_otg_core_if; ++typedef struct dwc_otg_core_if dwc_otg_core_if_t; ++ ++/** Maximum number of Periodic FIFOs */ ++#define MAX_PERIO_FIFOS 15 ++/** Maximum number of Periodic FIFOs */ ++#define MAX_TX_FIFOS 15 ++ ++/** Maximum number of Endpoints/HostChannels */ ++#define MAX_EPS_CHANNELS 16 ++ ++extern dwc_otg_core_if_t *dwc_otg_cil_init(const uint32_t * _reg_base_addr); ++extern void dwc_otg_core_init(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_cil_remove(dwc_otg_core_if_t * _core_if); ++ ++extern void dwc_otg_enable_global_interrupts(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_disable_global_interrupts(dwc_otg_core_if_t * _core_if); ++ ++extern uint8_t dwc_otg_is_device_mode(dwc_otg_core_if_t * _core_if); ++extern uint8_t dwc_otg_is_host_mode(dwc_otg_core_if_t * _core_if); ++ ++extern uint8_t dwc_otg_is_dma_enable(dwc_otg_core_if_t * core_if); ++ ++/** This function should be called on every hardware interrupt. */ ++extern int32_t dwc_otg_handle_common_intr(dwc_otg_core_if_t * _core_if); ++ ++/** @name OTG Core Parameters */ ++/** @{ */ ++ ++/** ++ * Specifies the OTG capabilities. The driver will automatically ++ * detect the value for this parameter if none is specified. ++ * 0 - HNP and SRP capable (default) ++ * 1 - SRP Only capable ++ * 2 - No HNP/SRP capable ++ */ ++extern int dwc_otg_set_param_otg_cap(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_param_otg_cap(dwc_otg_core_if_t * core_if); ++#define DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE 0 ++#define DWC_OTG_CAP_PARAM_SRP_ONLY_CAPABLE 1 ++#define DWC_OTG_CAP_PARAM_NO_HNP_SRP_CAPABLE 2 ++#define dwc_param_otg_cap_default DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE ++ ++extern int dwc_otg_set_param_opt(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_param_opt(dwc_otg_core_if_t * core_if); ++#define dwc_param_opt_default 1 ++ ++/** ++ * Specifies whether to use slave or DMA mode for accessing the data ++ * FIFOs. The driver will automatically detect the value for this ++ * parameter if none is specified. ++ * 0 - Slave ++ * 1 - DMA (default, if available) ++ */ ++extern int dwc_otg_set_param_dma_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_dma_enable(dwc_otg_core_if_t * core_if); ++#define dwc_param_dma_enable_default 1 ++ ++/** ++ * When DMA mode is enabled specifies whether to use ++ * address DMA or DMA Descritor mode for accessing the data ++ * FIFOs in device mode. The driver will automatically detect ++ * the value for this parameter if none is specified. ++ * 0 - address DMA ++ * 1 - DMA Descriptor(default, if available) ++ */ ++extern int dwc_otg_set_param_dma_desc_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_dma_desc_enable(dwc_otg_core_if_t * core_if); ++//#define dwc_param_dma_desc_enable_default 1 ++#define dwc_param_dma_desc_enable_default 0 // Broadcom BCM2708 ++ ++/** The DMA Burst size (applicable only for External DMA ++ * Mode). 1, 4, 8 16, 32, 64, 128, 256 (default 32) ++ */ ++extern int dwc_otg_set_param_dma_burst_size(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_dma_burst_size(dwc_otg_core_if_t * core_if); ++#define dwc_param_dma_burst_size_default 32 ++ ++/** ++ * Specifies the maximum speed of operation in host and device mode. ++ * The actual speed depends on the speed of the attached device and ++ * the value of phy_type. The actual speed depends on the speed of the ++ * attached device. ++ * 0 - High Speed (default) ++ * 1 - Full Speed ++ */ ++extern int dwc_otg_set_param_speed(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_param_speed(dwc_otg_core_if_t * core_if); ++#define dwc_param_speed_default 0 ++#define DWC_SPEED_PARAM_HIGH 0 ++#define DWC_SPEED_PARAM_FULL 1 ++ ++/** Specifies whether low power mode is supported when attached ++ * to a Full Speed or Low Speed device in host mode. ++ * 0 - Don't support low power mode (default) ++ * 1 - Support low power mode ++ */ ++extern int dwc_otg_set_param_host_support_fs_ls_low_power(dwc_otg_core_if_t * ++ core_if, int32_t val); ++extern int32_t dwc_otg_get_param_host_support_fs_ls_low_power(dwc_otg_core_if_t ++ * core_if); ++#define dwc_param_host_support_fs_ls_low_power_default 0 ++ ++/** Specifies the PHY clock rate in low power mode when connected to a ++ * Low Speed device in host mode. This parameter is applicable only if ++ * HOST_SUPPORT_FS_LS_LOW_POWER is enabled. If PHY_TYPE is set to FS ++ * then defaults to 6 MHZ otherwise 48 MHZ. ++ * ++ * 0 - 48 MHz ++ * 1 - 6 MHz ++ */ ++extern int dwc_otg_set_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * ++ core_if, int32_t val); ++extern int32_t dwc_otg_get_param_host_ls_low_power_phy_clk(dwc_otg_core_if_t * ++ core_if); ++#define dwc_param_host_ls_low_power_phy_clk_default 0 ++#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_48MHZ 0 ++#define DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ 1 ++ ++/** ++ * 0 - Use cC FIFO size parameters ++ * 1 - Allow dynamic FIFO sizing (default) ++ */ ++extern int dwc_otg_set_param_enable_dynamic_fifo(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_enable_dynamic_fifo(dwc_otg_core_if_t * ++ core_if); ++#define dwc_param_enable_dynamic_fifo_default 1 ++ ++/** Total number of 4-byte words in the data FIFO memory. This ++ * memory includes the Rx FIFO, non-periodic Tx FIFO, and periodic ++ * Tx FIFOs. ++ * 32 to 32768 (default 8192) ++ * Note: The total FIFO memory depth in the FPGA configuration is 8192. ++ */ ++extern int dwc_otg_set_param_data_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_data_fifo_size(dwc_otg_core_if_t * core_if); ++//#define dwc_param_data_fifo_size_default 8192 ++#define dwc_param_data_fifo_size_default 0xFF0 // Broadcom BCM2708 ++ ++/** Number of 4-byte words in the Rx FIFO in device mode when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1064) ++ */ ++extern int dwc_otg_set_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_dev_rx_fifo_size(dwc_otg_core_if_t * core_if); ++#define dwc_param_dev_rx_fifo_size_default 1064 ++ ++/** Number of 4-byte words in the non-periodic Tx FIFO in device mode ++ * when dynamic FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++extern int dwc_otg_set_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if, int32_t val); ++extern int32_t dwc_otg_get_param_dev_nperio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if); ++#define dwc_param_dev_nperio_tx_fifo_size_default 1024 ++ ++/** Number of 4-byte words in each of the periodic Tx FIFOs in device ++ * mode when dynamic FIFO sizing is enabled. ++ * 4 to 768 (default 256) ++ */ ++extern int dwc_otg_set_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val, int fifo_num); ++extern int32_t dwc_otg_get_param_dev_perio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if, int fifo_num); ++#define dwc_param_dev_perio_tx_fifo_size_default 256 ++ ++/** Number of 4-byte words in the Rx FIFO in host mode when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++extern int dwc_otg_set_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_host_rx_fifo_size(dwc_otg_core_if_t * core_if); ++//#define dwc_param_host_rx_fifo_size_default 1024 ++#define dwc_param_host_rx_fifo_size_default 774 // Broadcom BCM2708 ++ ++/** Number of 4-byte words in the non-periodic Tx FIFO in host mode ++ * when Dynamic FIFO sizing is enabled in the core. ++ * 16 to 32768 (default 1024) ++ */ ++extern int dwc_otg_set_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if, int32_t val); ++extern int32_t dwc_otg_get_param_host_nperio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if); ++//#define dwc_param_host_nperio_tx_fifo_size_default 1024 ++#define dwc_param_host_nperio_tx_fifo_size_default 0x100 // Broadcom BCM2708 ++ ++/** Number of 4-byte words in the host periodic Tx FIFO when dynamic ++ * FIFO sizing is enabled. ++ * 16 to 32768 (default 1024) ++ */ ++extern int dwc_otg_set_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if, int32_t val); ++extern int32_t dwc_otg_get_param_host_perio_tx_fifo_size(dwc_otg_core_if_t * ++ core_if); ++//#define dwc_param_host_perio_tx_fifo_size_default 1024 ++#define dwc_param_host_perio_tx_fifo_size_default 0x200 // Broadcom BCM2708 ++ ++/** The maximum transfer size supported in bytes. ++ * 2047 to 65,535 (default 65,535) ++ */ ++extern int dwc_otg_set_param_max_transfer_size(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_max_transfer_size(dwc_otg_core_if_t * core_if); ++#define dwc_param_max_transfer_size_default 65535 ++ ++/** The maximum number of packets in a transfer. ++ * 15 to 511 (default 511) ++ */ ++extern int dwc_otg_set_param_max_packet_count(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_max_packet_count(dwc_otg_core_if_t * core_if); ++#define dwc_param_max_packet_count_default 511 ++ ++/** The number of host channel registers to use. ++ * 1 to 16 (default 12) ++ * Note: The FPGA configuration supports a maximum of 12 host channels. ++ */ ++extern int dwc_otg_set_param_host_channels(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_host_channels(dwc_otg_core_if_t * core_if); ++#define dwc_param_host_channels_default 12 ++ ++/** The number of endpoints in addition to EP0 available for device ++ * mode operations. ++ * 1 to 15 (default 6 IN and OUT) ++ * Note: The FPGA configuration supports a maximum of 6 IN and OUT ++ * endpoints in addition to EP0. ++ */ ++extern int dwc_otg_set_param_dev_endpoints(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_dev_endpoints(dwc_otg_core_if_t * core_if); ++#define dwc_param_dev_endpoints_default 6 ++ ++/** ++ * Specifies the type of PHY interface to use. By default, the driver ++ * will automatically detect the phy_type. ++ * ++ * 0 - Full Speed PHY ++ * 1 - UTMI+ (default) ++ * 2 - ULPI ++ */ ++extern int dwc_otg_set_param_phy_type(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_param_phy_type(dwc_otg_core_if_t * core_if); ++#define DWC_PHY_TYPE_PARAM_FS 0 ++#define DWC_PHY_TYPE_PARAM_UTMI 1 ++#define DWC_PHY_TYPE_PARAM_ULPI 2 ++#define dwc_param_phy_type_default DWC_PHY_TYPE_PARAM_UTMI ++ ++/** ++ * Specifies the UTMI+ Data Width. This parameter is ++ * applicable for a PHY_TYPE of UTMI+ or ULPI. (For a ULPI ++ * PHY_TYPE, this parameter indicates the data width between ++ * the MAC and the ULPI Wrapper.) Also, this parameter is ++ * applicable only if the OTG_HSPHY_WIDTH cC parameter was set ++ * to "8 and 16 bits", meaning that the core has been ++ * configured to work at either data path width. ++ * ++ * 8 or 16 bits (default 16) ++ */ ++extern int dwc_otg_set_param_phy_utmi_width(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_phy_utmi_width(dwc_otg_core_if_t * core_if); ++//#define dwc_param_phy_utmi_width_default 16 ++#define dwc_param_phy_utmi_width_default 8 // Broadcom BCM2708 ++ ++/** ++ * Specifies whether the ULPI operates at double or single ++ * data rate. This parameter is only applicable if PHY_TYPE is ++ * ULPI. ++ * ++ * 0 - single data rate ULPI interface with 8 bit wide data ++ * bus (default) ++ * 1 - double data rate ULPI interface with 4 bit wide data ++ * bus ++ */ ++extern int dwc_otg_set_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_phy_ulpi_ddr(dwc_otg_core_if_t * core_if); ++#define dwc_param_phy_ulpi_ddr_default 0 ++ ++/** ++ * Specifies whether to use the internal or external supply to ++ * drive the vbus with a ULPI phy. ++ */ ++extern int dwc_otg_set_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_phy_ulpi_ext_vbus(dwc_otg_core_if_t * core_if); ++#define DWC_PHY_ULPI_INTERNAL_VBUS 0 ++#define DWC_PHY_ULPI_EXTERNAL_VBUS 1 ++#define dwc_param_phy_ulpi_ext_vbus_default DWC_PHY_ULPI_INTERNAL_VBUS ++ ++/** ++ * Specifies whether to use the I2Cinterface for full speed PHY. This ++ * parameter is only applicable if PHY_TYPE is FS. ++ * 0 - No (default) ++ * 1 - Yes ++ */ ++extern int dwc_otg_set_param_i2c_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_i2c_enable(dwc_otg_core_if_t * core_if); ++#define dwc_param_i2c_enable_default 0 ++ ++extern int dwc_otg_set_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_ulpi_fs_ls(dwc_otg_core_if_t * core_if); ++#define dwc_param_ulpi_fs_ls_default 0 ++ ++extern int dwc_otg_set_param_ts_dline(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_param_ts_dline(dwc_otg_core_if_t * core_if); ++#define dwc_param_ts_dline_default 0 ++ ++/** ++ * Specifies whether dedicated transmit FIFOs are ++ * enabled for non periodic IN endpoints in device mode ++ * 0 - No ++ * 1 - Yes ++ */ ++extern int dwc_otg_set_param_en_multiple_tx_fifo(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_en_multiple_tx_fifo(dwc_otg_core_if_t * ++ core_if); ++#define dwc_param_en_multiple_tx_fifo_default 1 ++ ++/** Number of 4-byte words in each of the Tx FIFOs in device ++ * mode when dynamic FIFO sizing is enabled. ++ * 4 to 768 (default 256) ++ */ ++extern int dwc_otg_set_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int fifo_num, int32_t val); ++extern int32_t dwc_otg_get_param_dev_tx_fifo_size(dwc_otg_core_if_t * core_if, ++ int fifo_num); ++#define dwc_param_dev_tx_fifo_size_default 256 ++ ++/** Thresholding enable flag- ++ * bit 0 - enable non-ISO Tx thresholding ++ * bit 1 - enable ISO Tx thresholding ++ * bit 2 - enable Rx thresholding ++ */ ++extern int dwc_otg_set_param_thr_ctl(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_thr_ctl(dwc_otg_core_if_t * core_if, int fifo_num); ++#define dwc_param_thr_ctl_default 0 ++ ++/** Thresholding length for Tx ++ * FIFOs in 32 bit DWORDs ++ */ ++extern int dwc_otg_set_param_tx_thr_length(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_tx_thr_length(dwc_otg_core_if_t * core_if); ++#define dwc_param_tx_thr_length_default 64 ++ ++/** Thresholding length for Rx ++ * FIFOs in 32 bit DWORDs ++ */ ++extern int dwc_otg_set_param_rx_thr_length(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_rx_thr_length(dwc_otg_core_if_t * core_if); ++#define dwc_param_rx_thr_length_default 64 ++ ++/** ++ * Specifies whether LPM (Link Power Management) support is enabled ++ */ ++extern int dwc_otg_set_param_lpm_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_lpm_enable(dwc_otg_core_if_t * core_if); ++#define dwc_param_lpm_enable_default 1 ++ ++/** ++ * Specifies whether PTI enhancement is enabled ++ */ ++extern int dwc_otg_set_param_pti_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_pti_enable(dwc_otg_core_if_t * core_if); ++#define dwc_param_pti_enable_default 0 ++ ++/** ++ * Specifies whether MPI enhancement is enabled ++ */ ++extern int dwc_otg_set_param_mpi_enable(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_mpi_enable(dwc_otg_core_if_t * core_if); ++#define dwc_param_mpi_enable_default 0 ++ ++/** ++ * Specifies whether IC_USB capability is enabled ++ */ ++extern int dwc_otg_set_param_ic_usb_cap(dwc_otg_core_if_t * core_if, ++ int32_t val); ++extern int32_t dwc_otg_get_param_ic_usb_cap(dwc_otg_core_if_t * core_if); ++#define dwc_param_ic_usb_cap_default 0 ++ ++extern int dwc_otg_set_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if, int32_t val); ++extern int32_t dwc_otg_get_param_ahb_thr_ratio(dwc_otg_core_if_t * core_if); ++#define dwc_param_ahb_thr_ratio_default 0 ++ ++/** @} */ ++ ++/** @name Access to registers and bit-fields */ ++ ++/** ++ * Dump core registers and SPRAM ++ */ ++extern void dwc_otg_dump_dev_registers(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_dump_spram(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_dump_host_registers(dwc_otg_core_if_t * _core_if); ++extern void dwc_otg_dump_global_registers(dwc_otg_core_if_t * _core_if); ++ ++/** ++ * Get host negotiation status. ++ */ ++extern uint32_t dwc_otg_get_hnpstatus(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get srp status ++ */ ++extern uint32_t dwc_otg_get_srpstatus(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Set hnpreq bit in the GOTGCTL register. ++ */ ++extern void dwc_otg_set_hnpreq(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get Content of SNPSID register. ++ */ ++extern uint32_t dwc_otg_get_gsnpsid(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get current mode. ++ * Returns 0 if in device mode, and 1 if in host mode. ++ */ ++extern uint32_t dwc_otg_get_mode(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get value of hnpcapable field in the GUSBCFG register ++ */ ++extern uint32_t dwc_otg_get_hnpcapable(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of hnpcapable field in the GUSBCFG register ++ */ ++extern void dwc_otg_set_hnpcapable(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of srpcapable field in the GUSBCFG register ++ */ ++extern uint32_t dwc_otg_get_srpcapable(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of srpcapable field in the GUSBCFG register ++ */ ++extern void dwc_otg_set_srpcapable(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of devspeed field in the DCFG register ++ */ ++extern uint32_t dwc_otg_get_devspeed(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of devspeed field in the DCFG register ++ */ ++extern void dwc_otg_set_devspeed(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get the value of busconnected field from the HPRT0 register ++ */ ++extern uint32_t dwc_otg_get_busconnected(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Gets the device enumeration Speed. ++ */ ++extern uint32_t dwc_otg_get_enumspeed(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get value of prtpwr field from the HPRT0 register ++ */ ++extern uint32_t dwc_otg_get_prtpower(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of prtpwr field from the HPRT0 register ++ */ ++extern void dwc_otg_set_prtpower(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of prtsusp field from the HPRT0 regsiter ++ */ ++extern uint32_t dwc_otg_get_prtsuspend(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of prtpwr field from the HPRT0 register ++ */ ++extern void dwc_otg_set_prtsuspend(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Set value of prtres field from the HPRT0 register ++ *FIXME Remove? ++ */ ++extern void dwc_otg_set_prtresume(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of rmtwkupsig bit in DCTL register ++ */ ++extern uint32_t dwc_otg_get_remotewakesig(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get value of prt_sleep_sts field from the GLPMCFG register ++ */ ++extern uint32_t dwc_otg_get_lpm_portsleepstatus(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get value of rem_wkup_en field from the GLPMCFG register ++ */ ++extern uint32_t dwc_otg_get_lpm_remotewakeenabled(dwc_otg_core_if_t * core_if); ++ ++/** ++ * Get value of appl_resp field from the GLPMCFG register ++ */ ++extern uint32_t dwc_otg_get_lpmresponse(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of appl_resp field from the GLPMCFG register ++ */ ++extern void dwc_otg_set_lpmresponse(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of hsic_connect field from the GLPMCFG register ++ */ ++extern uint32_t dwc_otg_get_hsic_connect(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of hsic_connect field from the GLPMCFG register ++ */ ++extern void dwc_otg_set_hsic_connect(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * Get value of inv_sel_hsic field from the GLPMCFG register. ++ */ ++extern uint32_t dwc_otg_get_inv_sel_hsic(dwc_otg_core_if_t * core_if); ++/** ++ * Set value of inv_sel_hsic field from the GLPMFG register. ++ */ ++extern void dwc_otg_set_inv_sel_hsic(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/* ++ * Some functions for accessing registers ++ */ ++ ++/** ++ * GOTGCTL register ++ */ ++extern uint32_t dwc_otg_get_gotgctl(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_gotgctl(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * GUSBCFG register ++ */ ++extern uint32_t dwc_otg_get_gusbcfg(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_gusbcfg(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * GRXFSIZ register ++ */ ++extern uint32_t dwc_otg_get_grxfsiz(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_grxfsiz(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * GNPTXFSIZ register ++ */ ++extern uint32_t dwc_otg_get_gnptxfsiz(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_gnptxfsiz(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++extern uint32_t dwc_otg_get_gpvndctl(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_gpvndctl(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * GGPIO register ++ */ ++extern uint32_t dwc_otg_get_ggpio(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_ggpio(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * GUID register ++ */ ++extern uint32_t dwc_otg_get_guid(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_guid(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * HPRT0 register ++ */ ++extern uint32_t dwc_otg_get_hprt0(dwc_otg_core_if_t * core_if); ++extern void dwc_otg_set_hprt0(dwc_otg_core_if_t * core_if, uint32_t val); ++ ++/** ++ * GHPTXFSIZE ++ */ ++extern uint32_t dwc_otg_get_hptxfsiz(dwc_otg_core_if_t * core_if); ++ ++/** @} */ ++ ++#endif /* __DWC_CORE_IF_H__ */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_dbg.h +@@ -0,0 +1,113 @@ ++/* ========================================================================== ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef __DWC_OTG_DBG_H__ ++#define __DWC_OTG_DBG_H__ ++ ++/** @file ++ * This file defines debug levels. ++ * Debugging support vanishes in non-debug builds. ++ */ ++ ++/** ++ * The Debug Level bit-mask variable. ++ */ ++extern uint32_t g_dbg_lvl; ++/** ++ * Set the Debug Level variable. ++ */ ++static inline uint32_t SET_DEBUG_LEVEL(const uint32_t new) ++{ ++ uint32_t old = g_dbg_lvl; ++ g_dbg_lvl = new; ++ return old; ++} ++ ++/** When debug level has the DBG_CIL bit set, display CIL Debug messages. */ ++#define DBG_CIL (0x2) ++/** When debug level has the DBG_CILV bit set, display CIL Verbose debug ++ * messages */ ++#define DBG_CILV (0x20) ++/** When debug level has the DBG_PCD bit set, display PCD (Device) debug ++ * messages */ ++#define DBG_PCD (0x4) ++/** When debug level has the DBG_PCDV set, display PCD (Device) Verbose debug ++ * messages */ ++#define DBG_PCDV (0x40) ++/** When debug level has the DBG_HCD bit set, display Host debug messages */ ++#define DBG_HCD (0x8) ++/** When debug level has the DBG_HCDV bit set, display Verbose Host debug ++ * messages */ ++#define DBG_HCDV (0x80) ++/** When debug level has the DBG_HCD_URB bit set, display enqueued URBs in host ++ * mode. */ ++#define DBG_HCD_URB (0x800) ++ ++/** When debug level has any bit set, display debug messages */ ++#define DBG_ANY (0xFF) ++ ++/** All debug messages off */ ++#define DBG_OFF 0 ++ ++/** Prefix string for DWC_DEBUG print macros. */ ++#define USB_DWC "DWC_otg: " ++ ++/** ++ * Print a debug message when the Global debug level variable contains ++ * the bit defined in <code>lvl</code>. ++ * ++ * @param[in] lvl - Debug level, use one of the DBG_ constants above. ++ * @param[in] x - like printf ++ * ++ * Example:<p> ++ * <code> ++ * DWC_DEBUGPL( DBG_ANY, "%s(%p)\n", __func__, _reg_base_addr); ++ * </code> ++ * <br> ++ * results in:<br> ++ * <code> ++ * usb-DWC_otg: dwc_otg_cil_init(ca867000) ++ * </code> ++ */ ++#ifdef DEBUG ++ ++# define DWC_DEBUGPL(lvl, x...) do{ if ((lvl)&g_dbg_lvl)__DWC_DEBUG(USB_DWC x ); }while(0) ++# define DWC_DEBUGP(x...) DWC_DEBUGPL(DBG_ANY, x ) ++ ++# define CHK_DEBUG_LEVEL(level) ((level) & g_dbg_lvl) ++ ++#else ++ ++# define DWC_DEBUGPL(lvl, x...) do{}while(0) ++# define DWC_DEBUGP(x...) ++ ++# define CHK_DEBUG_LEVEL(level) (0) ++ ++#endif /*DEBUG*/ ++#endif +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.c +@@ -0,0 +1,1577 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.c $ ++ * $Revision: #76 $ ++ * $Date: 2009/05/03 $ ++ * $Change: 1245589 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++/** @file ++ * The dwc_otg_driver module provides the initialization and cleanup entry ++ * points for the DWC_otg driver. This module will be dynamically installed ++ * after Linux is booted using the insmod command. When the module is ++ * installed, the dwc_otg_driver_init function is called. When the module is ++ * removed (using rmmod), the dwc_otg_driver_cleanup function is called. ++ * ++ * This module also defines a data structure for the dwc_otg_driver, which is ++ * used in conjunction with the standard ARM lm_device structure. These ++ * structures allow the OTG driver to comply with the standard Linux driver ++ * model in which devices and drivers are registered with a bus driver. This ++ * has the benefit that Linux can expose attributes of the driver and device ++ * in its special sysfs file system. Users can then read or write files in ++ * this file system to perform diagnostics on the driver components or the ++ * device. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/errno.h> ++#include <linux/types.h> ++#include <linux/stat.h> /* permission constants */ ++#include <linux/version.h> ++#include <linux/interrupt.h> ++ ++#ifdef LM_INTERFACE ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++#include <asm/arch/lm.h> ++#include <asm/arch/hardware.h> ++#else ++/* in 2.6.31, at least, we seem to have lost the generic LM infrastructure - ++ here we use definitions stolen from arm-integrator headers ++*/ ++#include <mach/lm.h> ++#include <mach/hardware.h> ++#endif ++#include <asm/sizes.h> ++#include <asm/mach/map.h> ++ ++#elif defined(PLATFORM_INTERFACE) ++ ++#include <linux/platform_device.h> ++#include <asm/mach/map.h> ++ ++#endif ++ ++# include <linux/irq.h> ++ ++#include <asm/io.h> ++ ++ ++#include "dwc_os.h" ++#include "dwc_otg_dbg.h" ++#include "dwc_otg_driver.h" ++#include "dwc_otg_attr.h" ++#include "dwc_otg_core_if.h" ++#include "dwc_otg_pcd_if.h" ++#include "dwc_otg_hcd_if.h" ++ ++#define DWC_DRIVER_VERSION "2.90b 6-MAY-2010" ++#define DWC_DRIVER_DESC "HS OTG USB Controller driver" ++ ++static const char dwc_driver_name[] = "dwc_otg"; ++ ++extern int pcd_init( ++#ifdef LM_INTERFACE ++ struct lm_device *_dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *_dev ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *dev ++#endif ++ ); ++extern int hcd_init( ++#ifdef LM_INTERFACE ++ struct lm_device *_dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *_dev ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *dev ++#endif ++ ); ++ ++extern int pcd_remove( ++#ifdef LM_INTERFACE ++ struct lm_device *_dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *_dev ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *_dev ++#endif ++ ); ++ ++extern void hcd_remove( ++#ifdef LM_INTERFACE ++ struct lm_device *_dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *_dev ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *_dev ++#endif ++ ); ++ ++/*-------------------------------------------------------------------------*/ ++/* Encapsulate the module parameter settings */ ++ ++struct dwc_otg_driver_module_params { ++ int32_t opt; ++ int32_t otg_cap; ++ int32_t dma_enable; ++ int32_t dma_desc_enable; ++ int32_t dma_burst_size; ++ int32_t speed; ++ int32_t host_support_fs_ls_low_power; ++ int32_t host_ls_low_power_phy_clk; ++ int32_t enable_dynamic_fifo; ++ int32_t data_fifo_size; ++ int32_t dev_rx_fifo_size; ++ int32_t dev_nperio_tx_fifo_size; ++ uint32_t dev_perio_tx_fifo_size[MAX_PERIO_FIFOS]; ++ int32_t host_rx_fifo_size; ++ int32_t host_nperio_tx_fifo_size; ++ int32_t host_perio_tx_fifo_size; ++ int32_t max_transfer_size; ++ int32_t max_packet_count; ++ int32_t host_channels; ++ int32_t dev_endpoints; ++ int32_t phy_type; ++ int32_t phy_utmi_width; ++ int32_t phy_ulpi_ddr; ++ int32_t phy_ulpi_ext_vbus; ++ int32_t i2c_enable; ++ int32_t ulpi_fs_ls; ++ int32_t ts_dline; ++ int32_t en_multiple_tx_fifo; ++ uint32_t dev_tx_fifo_size[MAX_TX_FIFOS]; ++ uint32_t thr_ctl; ++ uint32_t tx_thr_length; ++ uint32_t rx_thr_length; ++ int32_t pti_enable; ++ int32_t mpi_enable; ++ int32_t lpm_enable; ++ int32_t ic_usb_cap; ++ int32_t ahb_thr_ratio; ++}; ++ ++static struct dwc_otg_driver_module_params dwc_otg_module_params = { ++ .opt = -1, ++ .otg_cap = -1, ++ .dma_enable = -1, ++ .dma_desc_enable = -1, ++ .dma_burst_size = -1, ++ .speed = -1, ++ .host_support_fs_ls_low_power = -1, ++ .host_ls_low_power_phy_clk = -1, ++ .enable_dynamic_fifo = -1, ++ .data_fifo_size = -1, ++ .dev_rx_fifo_size = -1, ++ .dev_nperio_tx_fifo_size = -1, ++ .dev_perio_tx_fifo_size = { ++ /* dev_perio_tx_fifo_size_1 */ ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1 ++ /* 15 */ ++ }, ++ .host_rx_fifo_size = -1, ++ .host_nperio_tx_fifo_size = -1, ++ .host_perio_tx_fifo_size = -1, ++ .max_transfer_size = -1, ++ .max_packet_count = -1, ++ .host_channels = -1, ++ .dev_endpoints = -1, ++ .phy_type = -1, ++ .phy_utmi_width = -1, ++ .phy_ulpi_ddr = -1, ++ .phy_ulpi_ext_vbus = -1, ++ .i2c_enable = -1, ++ .ulpi_fs_ls = -1, ++ .ts_dline = -1, ++ .en_multiple_tx_fifo = -1, ++ .dev_tx_fifo_size = { ++ /* dev_tx_fifo_size */ ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1, ++ -1 ++ /* 15 */ ++ }, ++ .thr_ctl = -1, ++ .tx_thr_length = -1, ++ .rx_thr_length = -1, ++ .pti_enable = -1, ++ .mpi_enable = -1, ++ .lpm_enable = -1, ++ .ic_usb_cap = -1, ++ .ahb_thr_ratio = -1, ++}; ++ ++/** ++ * This function shows the Driver Version. ++ */ ++static ssize_t version_show(struct device_driver *dev, char *buf) ++{ ++ return snprintf(buf, sizeof(DWC_DRIVER_VERSION) + 2, "%s\n", ++ DWC_DRIVER_VERSION); ++} ++ ++static DRIVER_ATTR(version, S_IRUGO, version_show, NULL); ++ ++/** ++ * Global Debug Level Mask. ++ */ ++uint32_t g_dbg_lvl = 0; /* OFF */ ++ ++/** ++ * This function shows the driver Debug Level. ++ */ ++static ssize_t dbg_level_show(struct device_driver *drv, char *buf) ++{ ++ return sprintf(buf, "0x%0x\n", g_dbg_lvl); ++} ++ ++/** ++ * This function stores the driver Debug Level. ++ */ ++static ssize_t dbg_level_store(struct device_driver *drv, const char *buf, ++ size_t count) ++{ ++ g_dbg_lvl = simple_strtoul(buf, NULL, 16); ++ return count; ++} ++ ++static DRIVER_ATTR(debuglevel, S_IRUGO | S_IWUSR, dbg_level_show, ++ dbg_level_store); ++ ++/** ++ * This function is called during module intialization ++ * to pass module parameters to the DWC_OTG CORE. ++ */ ++static int set_parameters(dwc_otg_core_if_t * core_if) ++{ ++ int retval = 0; ++ int i; ++ ++ if (dwc_otg_module_params.otg_cap != -1) { ++ retval += ++ dwc_otg_set_param_otg_cap(core_if, ++ dwc_otg_module_params.otg_cap); ++ } ++ if (dwc_otg_module_params.dma_enable != -1) { ++ retval += ++ dwc_otg_set_param_dma_enable(core_if, ++ dwc_otg_module_params. ++ dma_enable); ++ } ++ if (dwc_otg_module_params.dma_desc_enable != -1) { ++ retval += ++ dwc_otg_set_param_dma_desc_enable(core_if, ++ dwc_otg_module_params. ++ dma_desc_enable); ++ } ++ if (dwc_otg_module_params.opt != -1) { ++ retval += ++ dwc_otg_set_param_opt(core_if, dwc_otg_module_params.opt); ++ } ++ if (dwc_otg_module_params.dma_burst_size != -1) { ++ retval += ++ dwc_otg_set_param_dma_burst_size(core_if, ++ dwc_otg_module_params. ++ dma_burst_size); ++ } ++ if (dwc_otg_module_params.host_support_fs_ls_low_power != -1) { ++ retval += ++ dwc_otg_set_param_host_support_fs_ls_low_power(core_if, ++ dwc_otg_module_params. ++ host_support_fs_ls_low_power); ++ } ++ if (dwc_otg_module_params.enable_dynamic_fifo != -1) { ++ retval += ++ dwc_otg_set_param_enable_dynamic_fifo(core_if, ++ dwc_otg_module_params. ++ enable_dynamic_fifo); ++ } ++ if (dwc_otg_module_params.data_fifo_size != -1) { ++ retval += ++ dwc_otg_set_param_data_fifo_size(core_if, ++ dwc_otg_module_params. ++ data_fifo_size); ++ } ++ if (dwc_otg_module_params.dev_rx_fifo_size != -1) { ++ retval += ++ dwc_otg_set_param_dev_rx_fifo_size(core_if, ++ dwc_otg_module_params. ++ dev_rx_fifo_size); ++ } ++ if (dwc_otg_module_params.dev_nperio_tx_fifo_size != -1) { ++ retval += ++ dwc_otg_set_param_dev_nperio_tx_fifo_size(core_if, ++ dwc_otg_module_params. ++ dev_nperio_tx_fifo_size); ++ } ++ if (dwc_otg_module_params.host_rx_fifo_size != -1) { ++ retval += ++ dwc_otg_set_param_host_rx_fifo_size(core_if, ++ dwc_otg_module_params.host_rx_fifo_size); ++ } ++ if (dwc_otg_module_params.host_nperio_tx_fifo_size != -1) { ++ retval += ++ dwc_otg_set_param_host_nperio_tx_fifo_size(core_if, ++ dwc_otg_module_params. ++ host_nperio_tx_fifo_size); ++ } ++ if (dwc_otg_module_params.host_perio_tx_fifo_size != -1) { ++ retval += ++ dwc_otg_set_param_host_perio_tx_fifo_size(core_if, ++ dwc_otg_module_params. ++ host_perio_tx_fifo_size); ++ } ++ if (dwc_otg_module_params.max_transfer_size != -1) { ++ retval += ++ dwc_otg_set_param_max_transfer_size(core_if, ++ dwc_otg_module_params. ++ max_transfer_size); ++ } ++ if (dwc_otg_module_params.max_packet_count != -1) { ++ retval += ++ dwc_otg_set_param_max_packet_count(core_if, ++ dwc_otg_module_params. ++ max_packet_count); ++ } ++ if (dwc_otg_module_params.host_channels != -1) { ++ retval += ++ dwc_otg_set_param_host_channels(core_if, ++ dwc_otg_module_params. ++ host_channels); ++ } ++ if (dwc_otg_module_params.dev_endpoints != -1) { ++ retval += ++ dwc_otg_set_param_dev_endpoints(core_if, ++ dwc_otg_module_params. ++ dev_endpoints); ++ } ++ if (dwc_otg_module_params.phy_type != -1) { ++ retval += ++ dwc_otg_set_param_phy_type(core_if, ++ dwc_otg_module_params.phy_type); ++ } ++ if (dwc_otg_module_params.speed != -1) { ++ retval += ++ dwc_otg_set_param_speed(core_if, ++ dwc_otg_module_params.speed); ++ } ++ if (dwc_otg_module_params.host_ls_low_power_phy_clk != -1) { ++ retval += ++ dwc_otg_set_param_host_ls_low_power_phy_clk(core_if, ++ dwc_otg_module_params. ++ host_ls_low_power_phy_clk); ++ } ++ if (dwc_otg_module_params.phy_ulpi_ddr != -1) { ++ retval += ++ dwc_otg_set_param_phy_ulpi_ddr(core_if, ++ dwc_otg_module_params. ++ phy_ulpi_ddr); ++ } ++ if (dwc_otg_module_params.phy_ulpi_ext_vbus != -1) { ++ retval += ++ dwc_otg_set_param_phy_ulpi_ext_vbus(core_if, ++ dwc_otg_module_params. ++ phy_ulpi_ext_vbus); ++ } ++ if (dwc_otg_module_params.phy_utmi_width != -1) { ++ retval += ++ dwc_otg_set_param_phy_utmi_width(core_if, ++ dwc_otg_module_params. ++ phy_utmi_width); ++ } ++ if (dwc_otg_module_params.ulpi_fs_ls != -1) { ++ retval += ++ dwc_otg_set_param_ulpi_fs_ls(core_if, dwc_otg_module_params.ulpi_fs_ls); ++ } ++ if (dwc_otg_module_params.ts_dline != -1) { ++ retval += ++ dwc_otg_set_param_ts_dline(core_if, ++ dwc_otg_module_params.ts_dline); ++ } ++ if (dwc_otg_module_params.i2c_enable != -1) { ++ retval += ++ dwc_otg_set_param_i2c_enable(core_if, ++ dwc_otg_module_params. ++ i2c_enable); ++ } ++ if (dwc_otg_module_params.en_multiple_tx_fifo != -1) { ++ retval += ++ dwc_otg_set_param_en_multiple_tx_fifo(core_if, ++ dwc_otg_module_params. ++ en_multiple_tx_fifo); ++ } ++ for (i = 0; i < 15; i++) { ++ if (dwc_otg_module_params.dev_perio_tx_fifo_size[i] != -1) { ++ retval += ++ dwc_otg_set_param_dev_perio_tx_fifo_size(core_if, ++ dwc_otg_module_params. ++ dev_perio_tx_fifo_size ++ [i], i); ++ } ++ } ++ ++ for (i = 0; i < 15; i++) { ++ if (dwc_otg_module_params.dev_tx_fifo_size[i] != -1) { ++ retval += dwc_otg_set_param_dev_tx_fifo_size(core_if, ++ dwc_otg_module_params. ++ dev_tx_fifo_size ++ [i], i); ++ } ++ } ++ if (dwc_otg_module_params.thr_ctl != -1) { ++ retval += ++ dwc_otg_set_param_thr_ctl(core_if, ++ dwc_otg_module_params.thr_ctl); ++ } ++ if (dwc_otg_module_params.mpi_enable != -1) { ++ retval += ++ dwc_otg_set_param_mpi_enable(core_if, ++ dwc_otg_module_params. ++ mpi_enable); ++ } ++ if (dwc_otg_module_params.pti_enable != -1) { ++ retval += ++ dwc_otg_set_param_pti_enable(core_if, ++ dwc_otg_module_params. ++ pti_enable); ++ } ++ if (dwc_otg_module_params.lpm_enable != -1) { ++ retval += ++ dwc_otg_set_param_lpm_enable(core_if, ++ dwc_otg_module_params. ++ lpm_enable); ++ } ++ if (dwc_otg_module_params.ic_usb_cap != -1) { ++ retval += ++ dwc_otg_set_param_ic_usb_cap(core_if, ++ dwc_otg_module_params. ++ ic_usb_cap); ++ } ++ if (dwc_otg_module_params.tx_thr_length != -1) { ++ retval += ++ dwc_otg_set_param_tx_thr_length(core_if, ++ dwc_otg_module_params.tx_thr_length); ++ } ++ if (dwc_otg_module_params.rx_thr_length != -1) { ++ retval += ++ dwc_otg_set_param_rx_thr_length(core_if, ++ dwc_otg_module_params. ++ rx_thr_length); ++ } ++ if(dwc_otg_module_params.ahb_thr_ratio != -1) { ++ retval += ++ dwc_otg_set_param_ahb_thr_ratio(core_if, dwc_otg_module_params.ahb_thr_ratio); ++ } ++ return retval; ++} ++ ++/** ++ * This function is the top level interrupt handler for the Common ++ * (Device and host modes) interrupts. ++ */ ++static irqreturn_t dwc_otg_common_irq(int irq, void *dev) ++{ ++ dwc_otg_device_t *otg_dev = dev; ++ int32_t retval = IRQ_NONE; ++ ++ retval = dwc_otg_handle_common_intr(otg_dev->core_if); ++ if (retval != 0) { ++ S3C2410X_CLEAR_EINTPEND(); ++ } ++ return IRQ_RETVAL(retval); ++} ++ ++/** ++ * This function is called when a lm_device is unregistered with the ++ * dwc_otg_driver. This happens, for example, when the rmmod command is ++ * executed. The device may or may not be electrically present. If it is ++ * present, the driver stops device processing. Any resources used on behalf ++ * of this device are freed. ++ * ++ * @param _dev ++ */ ++#ifdef LM_INTERFACE ++static void dwc_otg_driver_remove( ++ struct lm_device *_dev ++#elif defined(PCI_INTERFACE) ++static void dwc_otg_driver_remove( ++ struct pci_dev *_dev ++#elif defined(PLATFORM_INTERFACE) ++static int dwc_otg_driver_remove( ++ struct platform_device *_dev ++#endif ++) ++ ++{ ++#ifdef LM_INTERFACE ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); ++#endif ++ ++ ++ DWC_DEBUGPL(DBG_ANY, "%s(%p) otg_dev %p\n", __func__, _dev, otg_dev); ++ ++ if (!otg_dev) { ++ /* Memory allocation for the dwc_otg_device failed. */ ++ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__); ++#ifdef PLATFORM_INTERFACE ++ return -ENOMEM; ++#else ++ return; ++#endif ++ } ++#ifndef DWC_DEVICE_ONLY ++ if (otg_dev->hcd) { ++ hcd_remove(_dev); ++ } else { ++ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__); ++#ifdef PLATFORM_INTERFACE ++ return -EINVAL; ++#else ++ return; ++#endif ++ } ++#endif ++ ++#ifndef DWC_HOST_ONLY ++ if (otg_dev->pcd) { ++ pcd_remove(_dev); ++ } ++#endif ++ /* ++ * Free the IRQ ++ */ ++ if (otg_dev->common_irq_installed) { ++#ifdef PLATFORM_INTERFACE ++ free_irq(platform_get_irq(_dev, 0), otg_dev); ++#else ++ free_irq(_dev->irq, otg_dev); ++#endif ++ } ++ ++ if (otg_dev->core_if) { ++ dwc_otg_cil_remove(otg_dev->core_if); ++ } ++ ++ /* ++ * Remove the device attributes ++ */ ++ dwc_otg_attr_remove(_dev); ++ ++ /* ++ * Return the memory. ++ */ ++ if (otg_dev->base) { ++ iounmap(otg_dev->base); ++ } ++ dwc_free(otg_dev); ++ ++ /* ++ * Clear the drvdata pointer. ++ */ ++#ifdef LM_INTERFACE ++ lm_set_drvdata(_dev, 0); ++#elif defined(PCI_INTERFACE) ++ release_mem_region(otg_dev->rsrc_start, otg_dev->rsrc_len); ++ pci_set_drvdata(_dev, 0); ++#elif defined(PLATFORM_INTERFACE) ++ platform_set_drvdata(_dev, 0); ++ return 0; ++#endif ++} ++ ++/** ++ * This function is called when an lm_device is bound to a ++ * dwc_otg_driver. It creates the driver components required to ++ * control the device (CIL, HCD, and PCD) and it initializes the ++ * device. The driver components are stored in a dwc_otg_device ++ * structure. A reference to the dwc_otg_device is saved in the ++ * lm_device. This allows the driver to access the dwc_otg_device ++ * structure on subsequent calls to driver methods for this device. ++ * ++ * @param _dev Bus device ++ */ ++static int dwc_otg_driver_probe( ++#ifdef LM_INTERFACE ++struct lm_device *_dev ++#elif defined(PCI_INTERFACE) ++struct pci_dev *_dev, const struct pci_device_id *id ++#elif defined(PLATFORM_INTERFACE) ++struct platform_device *_dev ++#endif ++) ++{ ++ int retval = 0; ++ dwc_otg_device_t *dwc_otg_device; ++ int devirq; ++ ++ dev_dbg(&_dev->dev, "dwc_otg_driver_probe(%p)\n", _dev); ++#ifdef LM_INTERFACE ++ dev_dbg(&_dev->dev, "start=0x%08x\n", (unsigned)_dev->resource.start); ++#elif defined(PCI_INTERFACE) ++ if (!id) { ++ DWC_ERROR("Invalid pci_device_id %p", id); ++ return -EINVAL; ++ } ++ ++ if (!_dev || (pci_enable_device(_dev) < 0)) { ++ DWC_ERROR("Invalid pci_device %p", _dev); ++ return -ENODEV; ++ } ++ dev_dbg(&_dev->dev, "start=0x%08x\n", (unsigned)pci_resource_start(_dev,0)); ++ /* other stuff needed as well? */ ++ ++#elif defined(PLATFORM_INTERFACE) ++ dev_dbg(&_dev->dev, "start=0x%08x (len 0x%x)\n", ++ (unsigned)_dev->resource->start, ++ (unsigned)(_dev->resource->end - _dev->resource->start)); ++#endif ++ ++ ++ dwc_otg_device = dwc_alloc(sizeof(dwc_otg_device_t)); ++ ++ if (!dwc_otg_device) { ++ dev_err(&_dev->dev, "kmalloc of dwc_otg_device failed\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ memset(dwc_otg_device, 0, sizeof(*dwc_otg_device)); ++ dwc_otg_device->reg_offset = 0xFFFFFFFF; ++ ++ /* ++ * Map the DWC_otg Core memory into virtual address space. ++ */ ++#ifdef LM_INTERFACE ++#if 1 ++ dwc_otg_device->base = ioremap(_dev->resource.start, SZ_256K); ++#else ++ struct map_desc desc = { ++ .virtual = IO_ADDRESS((unsigned)_dev->resource.start), ++ .pfn = __phys_to_pfn((unsigned)_dev->resource.start), ++ .length = SZ_128K, ++ .type = MT_DEVICE ++ }; ++ iotable_init(&desc, 1); ++ dwc_otg_device->base = (void *)desc.virtual; ++#endif ++ ++ if (!dwc_otg_device->base) { ++ dev_err(&_dev->dev, "ioremap() failed\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ dev_dbg(&_dev->dev, "base=0x%08x\n", (unsigned)dwc_otg_device->base); ++#elif defined(PCI_INTERFACE) ++ _dev->current_state = PCI_D0; ++ _dev->dev.power.power_state = PMSG_ON; ++ ++ if (!_dev->irq) { ++ DWC_ERROR("Found HC with no IRQ. Check BIOS/PCI %s setup!", pci_name(_dev)); ++ retval = -ENODEV; ++ goto fail; ++ } ++ ++ dwc_otg_device->rsrc_start = pci_resource_start(_dev,0); ++ dwc_otg_device->rsrc_len = pci_resource_len(_dev,0); ++ DWC_DEBUGPL(DBG_ANY,"PCI resource: start=%08x, len=%08x\n", ++ dwc_otg_device->rsrc_start, ++ dwc_otg_device->rsrc_len); ++ if (!request_mem_region(dwc_otg_device->rsrc_start, dwc_otg_device->rsrc_len, "dwc_otg")) { ++ dev_dbg(&_dev->dev, "error mapping memory\n"); ++ retval = -EFAULT; ++ goto fail; ++ } ++ ++ dwc_otg_device->base = ioremap_nocache(dwc_otg_device->rsrc_start, dwc_otg_device->rsrc_len); ++ if (dwc_otg_device->base == NULL) { ++ dev_dbg(&_dev->dev, "error mapping memory\n"); ++ retval = -EFAULT; ++ goto fail; ++ } ++ dev_dbg(&_dev->dev, "base=0x%p (before adjust) \n", dwc_otg_device->base); ++ dwc_otg_device->base = (char *)dwc_otg_device->base; ++ dev_dbg(&_dev->dev, "base=0x%p (after adjust) \n", dwc_otg_device->base); ++ dev_dbg(&_dev->dev, "%s: mapped PA 0x%x to VA 0x%p\n", __func__, ++ (unsigned)dwc_otg_device->rsrc_start, dwc_otg_device->base); ++ // ++ pci_set_drvdata(_dev, dwc_otg_device); ++ pci_set_master(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ DWC_DEBUGPL(DBG_ANY,"Platform resource: start=%08x, len=%08x\n", ++ _dev->resource->start, ++ _dev->resource->end - _dev->resource->start + 1); ++#if 1 ++ if (!request_mem_region(_dev->resource->start, ++ _dev->resource->end - _dev->resource->start + 1, ++ "dwc_otg")) { ++ dev_dbg(&_dev->dev, "error reserving mapped memory\n"); ++ retval = -EFAULT; ++ goto fail; ++ } ++ ++ dwc_otg_device->base = ioremap_nocache(_dev->resource->start, ++ _dev->resource->end - ++ _dev->resource->start + 1); ++#else ++ { ++ struct map_desc desc = { ++ .virtual = IO_ADDRESS((unsigned)_dev->resource->start), ++ .pfn = __phys_to_pfn((unsigned)_dev->resource->start), ++ .length = SZ_128K, ++ .type = MT_DEVICE ++ }; ++ iotable_init(&desc, 1); ++ dwc_otg_device->base = (void *)desc.virtual; ++ } ++#endif ++ if (!dwc_otg_device->base) { ++ dev_err(&_dev->dev, "ioremap() failed\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ dev_dbg(&_dev->dev, "base=0x%08x\n", (unsigned)dwc_otg_device->base); ++#endif ++ ++ /* ++ * Initialize driver data to point to the global DWC_otg ++ * Device structure. ++ */ ++#ifdef LM_INTERFACE ++ lm_set_drvdata(_dev, dwc_otg_device); ++#elif defined(PLATFORM_INTERFACE) ++ platform_set_drvdata(_dev, dwc_otg_device); ++#endif ++ dev_dbg(&_dev->dev, "dwc_otg_device=0x%p\n", dwc_otg_device); ++ ++ dwc_otg_device->core_if = dwc_otg_cil_init(dwc_otg_device->base); ++ DWC_DEBUGPL(DBG_HCDV, "probe of device %p given core_if %p\n", ++ dwc_otg_device, dwc_otg_device->core_if);//GRAYG ++ ++ if (!dwc_otg_device->core_if) { ++ dev_err(&_dev->dev, "CIL initialization failed!\n"); ++ retval = -ENOMEM; ++ goto fail; ++ } ++ ++ dev_dbg(&_dev->dev, "Calling get_gsnpsid\n"); ++ /* ++ * Attempt to ensure this device is really a DWC_otg Controller. ++ * Read and verify the SNPSID register contents. The value should be ++ * 0x45F42XXX, which corresponds to "OT2", as in "OTG version 2.XX". ++ */ ++ ++ if ((dwc_otg_get_gsnpsid(dwc_otg_device->core_if) & 0xFFFFF000) != ++ 0x4F542000) { ++ dev_err(&_dev->dev, "Bad value for SNPSID: 0x%08x\n", ++ dwc_otg_get_gsnpsid(dwc_otg_device->core_if)); ++ dwc_otg_cil_remove(dwc_otg_device->core_if); ++ dwc_free(dwc_otg_device); ++ retval = -EINVAL; ++ goto fail; ++ } ++ ++ /* ++ * Validate parameter values. ++ */ ++ dev_dbg(&_dev->dev, "Calling set_parameters\n"); ++ if (set_parameters(dwc_otg_device->core_if)) { ++ dwc_otg_cil_remove(dwc_otg_device->core_if); ++ retval = -EINVAL; ++ goto fail; ++ } ++ ++ /* ++ * Create Device Attributes in sysfs ++ */ ++ dev_dbg(&_dev->dev, "Calling attr_create\n"); ++ dwc_otg_attr_create(_dev); ++ ++ /* ++ * Disable the global interrupt until all the interrupt ++ * handlers are installed. ++ */ ++ dev_dbg(&_dev->dev, "Calling disable_global_interrupts\n"); ++ dwc_otg_disable_global_interrupts(dwc_otg_device->core_if); ++ ++ /* ++ * Install the interrupt handler for the common interrupts before ++ * enabling common interrupts in core_init below. ++ */ ++#if defined(PLATFORM_INTERFACE) ++ devirq = platform_get_irq(_dev, 0); ++#else ++ devirq = _dev->irq; ++#endif ++ DWC_DEBUGPL(DBG_CIL, "registering (common) handler for irq%d\n", ++ devirq); ++ dev_dbg(&_dev->dev, "Calling request_irq(%d)\n", devirq); ++ retval = request_irq(devirq, dwc_otg_common_irq, ++ IRQF_SHARED, ++ "dwc_otg", dwc_otg_device); ++ if (retval) { ++ DWC_ERROR("request of irq%d failed\n", devirq); ++ retval = -EBUSY; ++ goto fail; ++ } else { ++ dwc_otg_device->common_irq_installed = 1; ++ } ++ ++#ifndef IRQF_TRIGGER_LOW ++#if defined(LM_INTERFACE) || defined(PLATFORM_INTERFACE) ++ dev_dbg(&_dev->dev, "Calling set_irq_type\n"); ++ set_irq_type(devirq, ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++ IRQT_LOW ++#else ++ IRQ_TYPE_LEVEL_LOW ++#endif ++ ); ++#endif ++#endif /*IRQF_TRIGGER_LOW*/ ++ ++ /* ++ * Initialize the DWC_otg core. ++ */ ++ dev_dbg(&_dev->dev, "Calling dwc_otg_core_init\n"); ++ dwc_otg_core_init(dwc_otg_device->core_if); ++ ++#ifndef DWC_HOST_ONLY ++ /* ++ * Initialize the PCD ++ */ ++ dev_dbg(&_dev->dev, "Calling pcd_init\n"); ++ retval = pcd_init(_dev); ++ if (retval != 0) { ++ DWC_ERROR("pcd_init failed\n"); ++ dwc_otg_device->pcd = NULL; ++ goto fail; ++ } ++#endif ++#ifndef DWC_DEVICE_ONLY ++ /* ++ * Initialize the HCD ++ */ ++ dev_dbg(&_dev->dev, "Calling hcd_init\n"); ++ retval = hcd_init(_dev); ++ if (retval != 0) { ++ DWC_ERROR("hcd_init failed\n"); ++ dwc_otg_device->hcd = NULL; ++ goto fail; ++ } ++#endif ++ /* Recover from drvdata having been overwritten by hcd_init() */ ++#ifdef LM_INTERFACE ++ lm_set_drvdata(_dev, dwc_otg_device); ++#elif defined(PLATFORM_INTERFACE) ++ platform_set_drvdata(_dev, dwc_otg_device); ++#elif defined(PCI_INTERFACE) ++ pci_set_drvdata(_dev, dwc_otg_device); ++#endif ++ ++ /* ++ * Enable the global interrupt after all the interrupt ++ * handlers are installed. ++ */ ++ dev_dbg(&_dev->dev, "Calling enable_global_interrupts\n"); ++ dwc_otg_enable_global_interrupts(dwc_otg_device->core_if); ++ dev_dbg(&_dev->dev, "Done\n"); ++ ++ return 0; ++ ++ fail: ++ dwc_otg_driver_remove(_dev); ++ return retval; ++} ++ ++/** ++ * This structure defines the methods to be called by a bus driver ++ * during the lifecycle of a device on that bus. Both drivers and ++ * devices are registered with a bus driver. The bus driver matches ++ * devices to drivers based on information in the device and driver ++ * structures. ++ * ++ * The probe function is called when the bus driver matches a device ++ * to this driver. The remove function is called when a device is ++ * unregistered with the bus driver. ++ */ ++#ifdef LM_INTERFACE ++static struct lm_driver dwc_otg_driver = { ++ .drv = { ++ .name = (char *)dwc_driver_name, ++ }, ++ .probe = dwc_otg_driver_probe, ++ .remove = dwc_otg_driver_remove, ++ // 'suspend' and 'resume' absent ++}; ++#elif defined(PCI_INTERFACE) ++static const struct pci_device_id pci_ids[] = { { ++ PCI_DEVICE(0x16c3, 0xabcd), ++ .driver_data = (unsigned long) 0xdeadbeef, ++ }, { /* end: all zeroes */ } ++}; ++MODULE_DEVICE_TABLE(pci, pci_ids); ++ ++/* pci driver glue; this is a "new style" PCI driver module */ ++static struct pci_driver dwc_otg_driver = { ++ .name = "dwc_otg", ++ .id_table = pci_ids, ++ ++ .probe = dwc_otg_driver_probe, ++ .remove = dwc_otg_driver_remove, ++ ++ .driver = { ++ .name = (char*)dwc_driver_name, ++ }, ++}; ++#elif defined(PLATFORM_INTERFACE) ++static struct platform_device_id platform_ids[] = { ++ { ++ .name = "bcm2708_usb", ++ .driver_data = (kernel_ulong_t) 0xdeadbeef, ++ }, ++ { /* end: all zeroes */ } ++}; ++MODULE_DEVICE_TABLE(platform, platform_ids); ++ ++static struct platform_driver dwc_otg_driver = { ++ .driver = { ++ .name = (char *)dwc_driver_name, ++ }, ++ .id_table = platform_ids, ++ ++ .probe = dwc_otg_driver_probe, ++ .remove = dwc_otg_driver_remove, ++ // no 'shutdown', 'suspend', 'resume', 'suspend_late' or 'resume_early' ++}; ++#endif ++ ++ ++/** ++ * This function is called when the dwc_otg_driver is installed with the ++ * insmod command. It registers the dwc_otg_driver structure with the ++ * appropriate bus driver. This will cause the dwc_otg_driver_probe function ++ * to be called. In addition, the bus driver will automatically expose ++ * attributes defined for the device and driver in the special sysfs file ++ * system. ++ * ++ * @return ++ */ ++static int __init dwc_otg_driver_init(void) ++{ ++ int retval = 0; ++ int error; ++ printk(KERN_INFO "%s: version %s (%s bus)\n", dwc_driver_name, ++ DWC_DRIVER_VERSION, ++#ifdef LM_INTERFACE ++ "logicmodule"); ++ retval = lm_driver_register(&dwc_otg_driver); ++#elif defined(PCI_INTERFACE) ++ "pci"); ++ retval = pci_register_driver(&dwc_otg_driver); ++#elif defined(PLATFORM_INTERFACE) ++ "platform"); ++ retval = platform_driver_register(&dwc_otg_driver); ++#endif ++ if (retval < 0) { ++ printk(KERN_ERR "%s retval=%d\n", __func__, retval); ++ return retval; ++ } ++#ifdef LM_INTERFACE ++ error = driver_create_file(&dwc_otg_driver.drv, &driver_attr_version); ++ error = driver_create_file(&dwc_otg_driver.drv, &driver_attr_debuglevel); ++#elif defined(PCI_INTERFACE) ++ error = driver_create_file(&dwc_otg_driver.driver, ++ &driver_attr_version); ++ error = driver_create_file(&dwc_otg_driver.driver, ++ &driver_attr_debuglevel); ++#elif defined(PLATFORM_INTERFACE) ++ error = driver_create_file(&dwc_otg_driver.driver, ++ &driver_attr_version); ++ error = driver_create_file(&dwc_otg_driver.driver, ++ &driver_attr_debuglevel); ++#endif ++ return retval; ++} ++ ++module_init(dwc_otg_driver_init); ++ ++/** ++ * This function is called when the driver is removed from the kernel ++ * with the rmmod command. The driver unregisters itself with its bus ++ * driver. ++ * ++ */ ++static void __exit dwc_otg_driver_cleanup(void) ++{ ++ printk(KERN_DEBUG "dwc_otg_driver_cleanup()\n"); ++ ++#ifdef LM_INTERFACE ++ driver_remove_file(&dwc_otg_driver.drv, &driver_attr_debuglevel); ++ driver_remove_file(&dwc_otg_driver.drv, &driver_attr_version); ++ lm_driver_unregister(&dwc_otg_driver); ++#elif defined(PCI_INTERFACE) ++ driver_remove_file(&dwc_otg_driver.driver, &driver_attr_debuglevel); ++ driver_remove_file(&dwc_otg_driver.driver, &driver_attr_version); ++ pci_unregister_driver(&dwc_otg_driver); ++#elif defined(PLATFORM_INTERFACE) ++ driver_remove_file(&dwc_otg_driver.driver, &driver_attr_debuglevel); ++ driver_remove_file(&dwc_otg_driver.driver, &driver_attr_version); ++ platform_driver_unregister(&dwc_otg_driver); ++#endif ++ ++ printk(KERN_INFO "%s module removed\n", dwc_driver_name); ++} ++module_exit(dwc_otg_driver_cleanup); ++ ++MODULE_DESCRIPTION(DWC_DRIVER_DESC); ++MODULE_AUTHOR("Synopsys Inc."); ++MODULE_LICENSE("GPL"); ++ ++module_param_named(otg_cap, dwc_otg_module_params.otg_cap, int, 0444); ++MODULE_PARM_DESC(otg_cap, "OTG Capabilities 0=HNP&SRP 1=SRP Only 2=None"); ++module_param_named(opt, dwc_otg_module_params.opt, int, 0444); ++MODULE_PARM_DESC(opt, "OPT Mode"); ++module_param_named(dma_enable, dwc_otg_module_params.dma_enable, int, 0444); ++MODULE_PARM_DESC(dma_enable, "DMA Mode 0=Slave 1=DMA enabled"); ++ ++module_param_named(dma_desc_enable, dwc_otg_module_params.dma_desc_enable, int, ++ 0444); ++MODULE_PARM_DESC(dma_desc_enable, ++ "DMA Desc Mode 0=Address DMA 1=DMA Descriptor enabled"); ++ ++module_param_named(dma_burst_size, dwc_otg_module_params.dma_burst_size, int, ++ 0444); ++MODULE_PARM_DESC(dma_burst_size, ++ "DMA Burst Size 1, 4, 8, 16, 32, 64, 128, 256"); ++module_param_named(speed, dwc_otg_module_params.speed, int, 0444); ++MODULE_PARM_DESC(speed, "Speed 0=High Speed 1=Full Speed"); ++module_param_named(host_support_fs_ls_low_power, ++ dwc_otg_module_params.host_support_fs_ls_low_power, int, ++ 0444); ++MODULE_PARM_DESC(host_support_fs_ls_low_power, ++ "Support Low Power w/FS or LS 0=Support 1=Don't Support"); ++module_param_named(host_ls_low_power_phy_clk, ++ dwc_otg_module_params.host_ls_low_power_phy_clk, int, 0444); ++MODULE_PARM_DESC(host_ls_low_power_phy_clk, ++ "Low Speed Low Power Clock 0=48Mhz 1=6Mhz"); ++module_param_named(enable_dynamic_fifo, ++ dwc_otg_module_params.enable_dynamic_fifo, int, 0444); ++MODULE_PARM_DESC(enable_dynamic_fifo, "0=cC Setting 1=Allow Dynamic Sizing"); ++module_param_named(data_fifo_size, dwc_otg_module_params.data_fifo_size, int, ++ 0444); ++MODULE_PARM_DESC(data_fifo_size, ++ "Total number of words in the data FIFO memory 32-32768"); ++module_param_named(dev_rx_fifo_size, dwc_otg_module_params.dev_rx_fifo_size, ++ int, 0444); ++MODULE_PARM_DESC(dev_rx_fifo_size, "Number of words in the Rx FIFO 16-32768"); ++module_param_named(dev_nperio_tx_fifo_size, ++ dwc_otg_module_params.dev_nperio_tx_fifo_size, int, 0444); ++MODULE_PARM_DESC(dev_nperio_tx_fifo_size, ++ "Number of words in the non-periodic Tx FIFO 16-32768"); ++module_param_named(dev_perio_tx_fifo_size_1, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[0], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_1, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_2, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[1], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_2, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_3, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[2], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_3, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_4, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[3], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_4, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_5, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[4], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_5, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_6, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[5], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_6, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_7, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[6], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_7, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_8, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[7], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_8, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_9, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[8], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_9, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_10, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[9], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_10, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_11, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[10], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_11, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_12, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[11], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_12, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_13, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[12], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_13, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_14, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[13], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_14, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(dev_perio_tx_fifo_size_15, ++ dwc_otg_module_params.dev_perio_tx_fifo_size[14], int, 0444); ++MODULE_PARM_DESC(dev_perio_tx_fifo_size_15, ++ "Number of words in the periodic Tx FIFO 4-768"); ++module_param_named(host_rx_fifo_size, dwc_otg_module_params.host_rx_fifo_size, ++ int, 0444); ++MODULE_PARM_DESC(host_rx_fifo_size, "Number of words in the Rx FIFO 16-32768"); ++module_param_named(host_nperio_tx_fifo_size, ++ dwc_otg_module_params.host_nperio_tx_fifo_size, int, 0444); ++MODULE_PARM_DESC(host_nperio_tx_fifo_size, ++ "Number of words in the non-periodic Tx FIFO 16-32768"); ++module_param_named(host_perio_tx_fifo_size, ++ dwc_otg_module_params.host_perio_tx_fifo_size, int, 0444); ++MODULE_PARM_DESC(host_perio_tx_fifo_size, ++ "Number of words in the host periodic Tx FIFO 16-32768"); ++module_param_named(max_transfer_size, dwc_otg_module_params.max_transfer_size, ++ int, 0444); ++/** @todo Set the max to 512K, modify checks */ ++MODULE_PARM_DESC(max_transfer_size, ++ "The maximum transfer size supported in bytes 2047-65535"); ++module_param_named(max_packet_count, dwc_otg_module_params.max_packet_count, ++ int, 0444); ++MODULE_PARM_DESC(max_packet_count, ++ "The maximum number of packets in a transfer 15-511"); ++module_param_named(host_channels, dwc_otg_module_params.host_channels, int, ++ 0444); ++MODULE_PARM_DESC(host_channels, ++ "The number of host channel registers to use 1-16"); ++module_param_named(dev_endpoints, dwc_otg_module_params.dev_endpoints, int, ++ 0444); ++MODULE_PARM_DESC(dev_endpoints, ++ "The number of endpoints in addition to EP0 available for device mode 1-15"); ++module_param_named(phy_type, dwc_otg_module_params.phy_type, int, 0444); ++MODULE_PARM_DESC(phy_type, "0=Reserved 1=UTMI+ 2=ULPI"); ++module_param_named(phy_utmi_width, dwc_otg_module_params.phy_utmi_width, int, ++ 0444); ++MODULE_PARM_DESC(phy_utmi_width, "Specifies the UTMI+ Data Width 8 or 16 bits"); ++module_param_named(phy_ulpi_ddr, dwc_otg_module_params.phy_ulpi_ddr, int, 0444); ++MODULE_PARM_DESC(phy_ulpi_ddr, ++ "ULPI at double or single data rate 0=Single 1=Double"); ++module_param_named(phy_ulpi_ext_vbus, dwc_otg_module_params.phy_ulpi_ext_vbus, ++ int, 0444); ++MODULE_PARM_DESC(phy_ulpi_ext_vbus, ++ "ULPI PHY using internal or external vbus 0=Internal"); ++module_param_named(i2c_enable, dwc_otg_module_params.i2c_enable, int, 0444); ++MODULE_PARM_DESC(i2c_enable, "FS PHY Interface"); ++module_param_named(ulpi_fs_ls, dwc_otg_module_params.ulpi_fs_ls, int, 0444); ++MODULE_PARM_DESC(ulpi_fs_ls, "ULPI PHY FS/LS mode only"); ++module_param_named(ts_dline, dwc_otg_module_params.ts_dline, int, 0444); ++MODULE_PARM_DESC(ts_dline, "Term select Dline pulsing for all PHYs"); ++module_param_named(debug, g_dbg_lvl, int, 0444); ++MODULE_PARM_DESC(debug, ""); ++ ++module_param_named(en_multiple_tx_fifo, ++ dwc_otg_module_params.en_multiple_tx_fifo, int, 0444); ++MODULE_PARM_DESC(en_multiple_tx_fifo, ++ "Dedicated Non Periodic Tx FIFOs 0=disabled 1=enabled"); ++module_param_named(dev_tx_fifo_size_1, ++ dwc_otg_module_params.dev_tx_fifo_size[0], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_1, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_2, ++ dwc_otg_module_params.dev_tx_fifo_size[1], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_2, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_3, ++ dwc_otg_module_params.dev_tx_fifo_size[2], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_3, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_4, ++ dwc_otg_module_params.dev_tx_fifo_size[3], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_4, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_5, ++ dwc_otg_module_params.dev_tx_fifo_size[4], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_5, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_6, ++ dwc_otg_module_params.dev_tx_fifo_size[5], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_6, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_7, ++ dwc_otg_module_params.dev_tx_fifo_size[6], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_7, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_8, ++ dwc_otg_module_params.dev_tx_fifo_size[7], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_8, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_9, ++ dwc_otg_module_params.dev_tx_fifo_size[8], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_9, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_10, ++ dwc_otg_module_params.dev_tx_fifo_size[9], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_10, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_11, ++ dwc_otg_module_params.dev_tx_fifo_size[10], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_11, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_12, ++ dwc_otg_module_params.dev_tx_fifo_size[11], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_12, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_13, ++ dwc_otg_module_params.dev_tx_fifo_size[12], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_13, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_14, ++ dwc_otg_module_params.dev_tx_fifo_size[13], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_14, "Number of words in the Tx FIFO 4-768"); ++module_param_named(dev_tx_fifo_size_15, ++ dwc_otg_module_params.dev_tx_fifo_size[14], int, 0444); ++MODULE_PARM_DESC(dev_tx_fifo_size_15, "Number of words in the Tx FIFO 4-768"); ++ ++module_param_named(thr_ctl, dwc_otg_module_params.thr_ctl, int, 0444); ++MODULE_PARM_DESC(thr_ctl, ++ "Thresholding enable flag bit 0 - non ISO Tx thr., 1 - ISO Tx thr., 2 - Rx thr.- bit 0=disabled 1=enabled"); ++module_param_named(tx_thr_length, dwc_otg_module_params.tx_thr_length, int, ++ 0444); ++MODULE_PARM_DESC(tx_thr_length, "Tx Threshold length in 32 bit DWORDs"); ++module_param_named(rx_thr_length, dwc_otg_module_params.rx_thr_length, int, ++ 0444); ++MODULE_PARM_DESC(rx_thr_length, "Rx Threshold length in 32 bit DWORDs"); ++ ++module_param_named(pti_enable, dwc_otg_module_params.pti_enable, int, 0444); ++module_param_named(mpi_enable, dwc_otg_module_params.mpi_enable, int, 0444); ++module_param_named(lpm_enable, dwc_otg_module_params.lpm_enable, int, 0444); ++MODULE_PARM_DESC(lpm_enable, "LPM Enable 0=LPM Disabled 1=LPM Enabled"); ++module_param_named(ic_usb_cap, dwc_otg_module_params.ic_usb_cap, int, 0444); ++MODULE_PARM_DESC(ic_usb_cap, ++ "IC_USB Capability 0=IC_USB Disabled 1=IC_USB Enabled"); ++module_param_named(ahb_thr_ratio, dwc_otg_module_params.ahb_thr_ratio, int, 0444); ++MODULE_PARM_DESC(ahb_thr_ratio, "AHB Threshold Ratio"); ++ ++/** @page "Module Parameters" ++ * ++ * The following parameters may be specified when starting the module. ++ * These parameters define how the DWC_otg controller should be ++ * configured. Parameter values are passed to the CIL initialization ++ * function dwc_otg_cil_init ++ * ++ * Example: <code>modprobe dwc_otg speed=1 otg_cap=1</code> ++ * ++ ++ <table> ++ <tr><td>Parameter Name</td><td>Meaning</td></tr> ++ ++ <tr> ++ <td>otg_cap</td> ++ <td>Specifies the OTG capabilities. The driver will automatically detect the ++ value for this parameter if none is specified. ++ - 0: HNP and SRP capable (default, if available) ++ - 1: SRP Only capable ++ - 2: No HNP/SRP capable ++ </td></tr> ++ ++ <tr> ++ <td>dma_enable</td> ++ <td>Specifies whether to use slave or DMA mode for accessing the data FIFOs. ++ The driver will automatically detect the value for this parameter if none is ++ specified. ++ - 0: Slave ++ - 1: DMA (default, if available) ++ </td></tr> ++ ++ <tr> ++ <td>dma_burst_size</td> ++ <td>The DMA Burst size (applicable only for External DMA Mode). ++ - Values: 1, 4, 8 16, 32, 64, 128, 256 (default 32) ++ </td></tr> ++ ++ <tr> ++ <td>speed</td> ++ <td>Specifies the maximum speed of operation in host and device mode. The ++ actual speed depends on the speed of the attached device and the value of ++ phy_type. ++ - 0: High Speed (default) ++ - 1: Full Speed ++ </td></tr> ++ ++ <tr> ++ <td>host_support_fs_ls_low_power</td> ++ <td>Specifies whether low power mode is supported when attached to a Full ++ Speed or Low Speed device in host mode. ++ - 0: Don't support low power mode (default) ++ - 1: Support low power mode ++ </td></tr> ++ ++ <tr> ++ <td>host_ls_low_power_phy_clk</td> ++ <td>Specifies the PHY clock rate in low power mode when connected to a Low ++ Speed device in host mode. This parameter is applicable only if ++ HOST_SUPPORT_FS_LS_LOW_POWER is enabled. ++ - 0: 48 MHz (default) ++ - 1: 6 MHz ++ </td></tr> ++ ++ <tr> ++ <td>enable_dynamic_fifo</td> ++ <td> Specifies whether FIFOs may be resized by the driver software. ++ - 0: Use cC FIFO size parameters ++ - 1: Allow dynamic FIFO sizing (default) ++ </td></tr> ++ ++ <tr> ++ <td>data_fifo_size</td> ++ <td>Total number of 4-byte words in the data FIFO memory. This memory ++ includes the Rx FIFO, non-periodic Tx FIFO, and periodic Tx FIFOs. ++ - Values: 32 to 32768 (default 8192) ++ ++ Note: The total FIFO memory depth in the FPGA configuration is 8192. ++ </td></tr> ++ ++ <tr> ++ <td>dev_rx_fifo_size</td> ++ <td>Number of 4-byte words in the Rx FIFO in device mode when dynamic ++ FIFO sizing is enabled. ++ - Values: 16 to 32768 (default 1064) ++ </td></tr> ++ ++ <tr> ++ <td>dev_nperio_tx_fifo_size</td> ++ <td>Number of 4-byte words in the non-periodic Tx FIFO in device mode when ++ dynamic FIFO sizing is enabled. ++ - Values: 16 to 32768 (default 1024) ++ </td></tr> ++ ++ <tr> ++ <td>dev_perio_tx_fifo_size_n (n = 1 to 15)</td> ++ <td>Number of 4-byte words in each of the periodic Tx FIFOs in device mode ++ when dynamic FIFO sizing is enabled. ++ - Values: 4 to 768 (default 256) ++ </td></tr> ++ ++ <tr> ++ <td>host_rx_fifo_size</td> ++ <td>Number of 4-byte words in the Rx FIFO in host mode when dynamic FIFO ++ sizing is enabled. ++ - Values: 16 to 32768 (default 1024) ++ </td></tr> ++ ++ <tr> ++ <td>host_nperio_tx_fifo_size</td> ++ <td>Number of 4-byte words in the non-periodic Tx FIFO in host mode when ++ dynamic FIFO sizing is enabled in the core. ++ - Values: 16 to 32768 (default 1024) ++ </td></tr> ++ ++ <tr> ++ <td>host_perio_tx_fifo_size</td> ++ <td>Number of 4-byte words in the host periodic Tx FIFO when dynamic FIFO ++ sizing is enabled. ++ - Values: 16 to 32768 (default 1024) ++ </td></tr> ++ ++ <tr> ++ <td>max_transfer_size</td> ++ <td>The maximum transfer size supported in bytes. ++ - Values: 2047 to 65,535 (default 65,535) ++ </td></tr> ++ ++ <tr> ++ <td>max_packet_count</td> ++ <td>The maximum number of packets in a transfer. ++ - Values: 15 to 511 (default 511) ++ </td></tr> ++ ++ <tr> ++ <td>host_channels</td> ++ <td>The number of host channel registers to use. ++ - Values: 1 to 16 (default 12) ++ ++ Note: The FPGA configuration supports a maximum of 12 host channels. ++ </td></tr> ++ ++ <tr> ++ <td>dev_endpoints</td> ++ <td>The number of endpoints in addition to EP0 available for device mode ++ operations. ++ - Values: 1 to 15 (default 6 IN and OUT) ++ ++ Note: The FPGA configuration supports a maximum of 6 IN and OUT endpoints in ++ addition to EP0. ++ </td></tr> ++ ++ <tr> ++ <td>phy_type</td> ++ <td>Specifies the type of PHY interface to use. By default, the driver will ++ automatically detect the phy_type. ++ - 0: Full Speed ++ - 1: UTMI+ (default, if available) ++ - 2: ULPI ++ </td></tr> ++ ++ <tr> ++ <td>phy_utmi_width</td> ++ <td>Specifies the UTMI+ Data Width. This parameter is applicable for a ++ phy_type of UTMI+. Also, this parameter is applicable only if the ++ OTG_HSPHY_WIDTH cC parameter was set to "8 and 16 bits", meaning that the ++ core has been configured to work at either data path width. ++ - Values: 8 or 16 bits (default 16) ++ </td></tr> ++ ++ <tr> ++ <td>phy_ulpi_ddr</td> ++ <td>Specifies whether the ULPI operates at double or single data rate. This ++ parameter is only applicable if phy_type is ULPI. ++ - 0: single data rate ULPI interface with 8 bit wide data bus (default) ++ - 1: double data rate ULPI interface with 4 bit wide data bus ++ </td></tr> ++ ++ <tr> ++ <td>i2c_enable</td> ++ <td>Specifies whether to use the I2C interface for full speed PHY. This ++ parameter is only applicable if PHY_TYPE is FS. ++ - 0: Disabled (default) ++ - 1: Enabled ++ </td></tr> ++ ++ <tr> ++ <td>otg_en_multiple_tx_fifo</td> ++ <td>Specifies whether dedicatedto tx fifos are enabled for non periodic IN EPs. ++ The driver will automatically detect the value for this parameter if none is ++ specified. ++ - 0: Disabled ++ - 1: Enabled (default, if available) ++ </td></tr> ++ ++ <tr> ++ <td>dev_tx_fifo_size_n (n = 1 to 15)</td> ++ <td>Number of 4-byte words in each of the Tx FIFOs in device mode ++ when dynamic FIFO sizing is enabled. ++ - Values: 4 to 768 (default 256) ++ </td></tr> ++ ++ <tr> ++ <td>tx_thr_length</td> ++ <td>Transmit Threshold length in 32 bit double words ++ - Values: 8 to 128 (default 64) ++ </td></tr> ++ ++ <tr> ++ <td>rx_thr_length</td> ++ <td>Receive Threshold length in 32 bit double words ++ - Values: 8 to 128 (default 64) ++ </td></tr> ++ ++<tr> ++ <td>thr_ctl</td> ++ <td>Specifies whether to enable Thresholding for Device mode. Bits 0, 1, 2 of this ++ parmater specifies if thresholding is enabled for non-Iso Tx, Iso Tx and Rx ++ transfers accordingly. ++ The driver will automatically detect the value for this parameter if none is ++ specified. ++ - Values: 0 to 7 (default 0) ++ Bit values indicate: ++ - 0: Thresholding disabled ++ - 1: Thresholding enabled ++ </td></tr> ++ ++<tr> ++ <td>dma_desc_enable</td> ++ <td>Specifies whether to enable Descriptor DMA mode. ++ The driver will automatically detect the value for this parameter if none is ++ specified. ++ - 0: Descriptor DMA disabled ++ - 1: Descriptor DMA (default, if available) ++ </td></tr> ++ ++<tr> ++ <td>mpi_enable</td> ++ <td>Specifies whether to enable MPI enhancement mode. ++ The driver will automatically detect the value for this parameter if none is ++ specified. ++ - 0: MPI disabled (default) ++ - 1: MPI enable ++ </td></tr> ++ ++<tr> ++ <td>pti_enable</td> ++ <td>Specifies whether to enable PTI enhancement support. ++ The driver will automatically detect the value for this parameter if none is ++ specified. ++ - 0: PTI disabled (default) ++ - 1: PTI enable ++ </td></tr> ++ ++<tr> ++ <td>lpm_enable</td> ++ <td>Specifies whether to enable LPM support. ++ The driver will automatically detect the value for this parameter if none is ++ specified. ++ - 0: LPM disabled ++ - 1: LPM enable (default, if available) ++ </td></tr> ++ ++ <tr> ++ <td>ahb_thr_ratio</td> ++ <td>Specifies AHB Threshold ratio. ++ - Values: 0 to 3 (default 0) ++ </td></tr> ++ ++*/ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_driver.h +@@ -0,0 +1,101 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_driver.h $ ++ * $Revision: #16 $ ++ * $Date: 2009/04/03 $ ++ * $Change: 1225160 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef __DWC_OTG_DRIVER_H__ ++#define __DWC_OTG_DRIVER_H__ ++ ++/** @file ++ * This file contains the interface to the Linux driver. ++ */ ++#include "dwc_otg_core_if.h" ++ ++/* Type declarations */ ++struct dwc_otg_pcd; ++struct dwc_otg_hcd; ++ ++#ifdef PCI_INTERFACE ++#include <linux/pci.h> ++#endif ++ ++ ++ ++/** ++ * This structure is a wrapper that encapsulates the driver components used to ++ * manage a single DWC_otg controller. ++ */ ++typedef struct dwc_otg_device { ++ /** Base address returned from ioremap() */ ++ void *base; ++ ++#ifdef LM_INTERFACE ++ struct lm_device *lmdev; ++#elif defined(PCI_INTERFACE) ++ int rsrc_start; ++ int rsrc_len; ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *platformdev; ++#endif ++ ++ /** Pointer to the core interface structure. */ ++ dwc_otg_core_if_t *core_if; ++ ++ /** Register offset for Diagnostic API. */ ++ uint32_t reg_offset; ++ ++ /** Pointer to the PCD structure. */ ++ struct dwc_otg_pcd *pcd; ++ ++ /** Pointer to the HCD structure. */ ++ struct dwc_otg_hcd *hcd; ++ ++ /** Flag to indicate whether the common IRQ handler is installed. */ ++ uint8_t common_irq_installed; ++ ++} dwc_otg_device_t; ++ ++/*We must clear S3C24XX_EINTPEND external interrupt register ++ * because after clearing in this register trigerred IRQ from ++ * H/W core in kernel interrupt can be occured again before OTG ++ * handlers clear all IRQ sources of Core registers because of ++ * timing latencies and Low Level IRQ Type. ++ */ ++#ifdef CONFIG_MACH_IPMATE ++#define S3C2410X_CLEAR_EINTPEND() \ ++do { \ ++ __raw_writel(1UL << 11,S3C24XX_EINTPEND); \ ++} while (0) ++#else ++#define S3C2410X_CLEAR_EINTPEND() do { } while (0) ++#endif ++ ++#endif +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c +@@ -0,0 +1,3330 @@ ++ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.c $ ++ * $Revision: #87 $ ++ * $Date: 2009/04/23 $ ++ * $Change: 1239143 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++/** @file ++ * This file implements HCD Core. All code in this file is portable and don't ++ * use any OS specific functions. ++ * Interface provided by HCD Core is defined in <code><hcd_if.h></code> ++ * header file. ++ */ ++ ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++#ifdef HW2937_WORKAROUND ++//#include <linux/kernel.h> ++#include <linux/spinlock.h> ++#endif ++ ++dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void) ++{ ++ return dwc_alloc(sizeof(dwc_otg_hcd_t)); ++} ++ ++/** ++ * Connection timeout function. An OTG host is required to display a ++ * message if the device does not connect within 10 seconds. ++ */ ++void dwc_otg_hcd_connect_timeout(void *ptr) ++{ ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, ptr); ++ DWC_PRINTF("Connect Timeout\n"); ++ __DWC_ERROR("Device Not Connected/Responding\n"); ++} ++ ++#ifdef DEBUG ++static void dump_channel_info(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ if (qh->channel != NULL) { ++ dwc_hc_t *hc = qh->channel; ++ dwc_list_link_t *item; ++ dwc_otg_qh_t *qh_item; ++ int num_channels = hcd->core_if->core_params->host_channels; ++ int i; ++ ++ dwc_otg_hc_regs_t *hc_regs; ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ hctsiz_data_t hctsiz; ++ uint32_t hcdma; ++ ++ hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcsplt.d32 = dwc_read_reg32(&hc_regs->hcsplt); ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ hcdma = dwc_read_reg32(&hc_regs->hcdma); ++ ++ DWC_PRINTF(" Assigned to channel %p:\n", hc); ++ DWC_PRINTF(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, ++ hcsplt.d32); ++ DWC_PRINTF(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, ++ hcdma); ++ DWC_PRINTF(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n", ++ hc->dev_addr, hc->ep_num, hc->ep_is_in); ++ DWC_PRINTF(" ep_type: %d\n", hc->ep_type); ++ DWC_PRINTF(" max_packet: %d\n", hc->max_packet); ++ DWC_PRINTF(" data_pid_start: %d\n", hc->data_pid_start); ++ DWC_PRINTF(" xfer_started: %d\n", hc->xfer_started); ++ DWC_PRINTF(" halt_status: %d\n", hc->halt_status); ++ DWC_PRINTF(" xfer_buff: %p\n", hc->xfer_buff); ++ DWC_PRINTF(" xfer_len: %d\n", hc->xfer_len); ++ DWC_PRINTF(" qh: %p\n", hc->qh); ++ DWC_PRINTF(" NP inactive sched:\n"); ++ DWC_LIST_FOREACH(item, &hcd->non_periodic_sched_inactive) { ++ qh_item = ++ DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry); ++ DWC_PRINTF(" %p\n", qh_item); ++ } ++ DWC_PRINTF(" NP active sched:\n"); ++ DWC_LIST_FOREACH(item, &hcd->non_periodic_sched_active) { ++ qh_item = ++ DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry); ++ DWC_PRINTF(" %p\n", qh_item); ++ } ++ DWC_PRINTF(" Channels: \n"); ++ for (i = 0; i < num_channels; i++) { ++ dwc_hc_t *hc = hcd->hc_ptr_array[i]; ++ DWC_PRINTF(" %2d: %p\n", i, hc); ++ } ++ } ++} ++#endif /* DEBUG */ ++ ++/** ++ * Work queue function for starting the HCD when A-Cable is connected. ++ * The hcd_start() must be called in a process context. ++ */ ++static void hcd_start_func(void *_vp) ++{ ++ dwc_otg_hcd_t *hcd = (dwc_otg_hcd_t *) _vp; ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s() %p\n", __func__, hcd); ++ if (hcd) { ++ hcd->fops->start(hcd); ++ } ++} ++ ++static void del_xfer_timers(dwc_otg_hcd_t * hcd) ++{ ++#ifdef DEBUG ++ int i; ++ int num_channels = hcd->core_if->core_params->host_channels; ++ for (i = 0; i < num_channels; i++) { ++ DWC_TIMER_CANCEL(hcd->core_if->hc_xfer_timer[i]); ++ } ++#endif ++} ++ ++static void del_timers(dwc_otg_hcd_t * hcd) ++{ ++ del_xfer_timers(hcd); ++ DWC_TIMER_CANCEL(hcd->conn_timer); ++} ++ ++/** ++ * Processes all the URBs in a single list of QHs. Completes them with ++ * -ETIMEDOUT and frees the QTD. ++ */ ++static void kill_urbs_in_qh_list(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list) ++{ ++ dwc_list_link_t *qh_item; ++ dwc_otg_qh_t *qh; ++ dwc_otg_qtd_t *qtd, *qtd_tmp; ++ ++ DWC_LIST_FOREACH(qh_item, qh_list) { ++ qh = DWC_LIST_ENTRY(qh_item, dwc_otg_qh_t, qh_list_entry); ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, ++ &qh->qtd_list, qtd_list_entry) { ++ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ if (qtd->urb != NULL) { ++ hcd->fops->complete(hcd, qtd->urb->priv, ++ qtd->urb, ++ -DWC_E_TIMEOUT); ++ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); ++ } ++ ++ } ++ } ++} ++ ++/** ++ * Responds with an error status of ETIMEDOUT to all URBs in the non-periodic ++ * and periodic schedules. The QTD associated with each URB is removed from ++ * the schedule and freed. This function may be called when a disconnect is ++ * detected or when the HCD is being stopped. ++ */ ++static void kill_all_urbs(dwc_otg_hcd_t * hcd) ++{ ++ kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_inactive); ++ kill_urbs_in_qh_list(hcd, &hcd->non_periodic_sched_active); ++ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_inactive); ++ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_ready); ++ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_assigned); ++ kill_urbs_in_qh_list(hcd, &hcd->periodic_sched_queued); ++} ++ ++/** ++ * Start the connection timer. An OTG host is required to display a ++ * message if the device does not connect within 10 seconds. The ++ * timer is deleted if a port connect interrupt occurs before the ++ * timer expires. ++ */ ++static void dwc_otg_hcd_start_connect_timer(dwc_otg_hcd_t * hcd) ++{ ++ DWC_TIMER_SCHEDULE(hcd->conn_timer, 10000 /* 10 secs */ ); ++} ++ ++/** ++ * HCD Callback function for disconnect of the HCD. ++ * ++ * @param p void pointer to the <code>struct usb_hcd</code> ++ */ ++static int32_t dwc_otg_hcd_session_start_cb(void *p) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd; ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p); ++ dwc_otg_hcd = p; ++ dwc_otg_hcd_start_connect_timer(dwc_otg_hcd); ++ return 1; ++} ++ ++/** ++ * HCD Callback function for starting the HCD when A-Cable is ++ * connected. ++ * ++ * @param p void pointer to the <code>struct usb_hcd</code> ++ */ ++static int32_t dwc_otg_hcd_start_cb(void *p) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = p; ++ dwc_otg_core_if_t *core_if; ++ hprt0_data_t hprt0; ++ ++ core_if = dwc_otg_hcd->core_if; ++ ++ if (core_if->op_state == B_HOST) { ++ /* ++ * Reset the port. During a HNP mode switch the reset ++ * needs to occur within 1ms and have a duration of at ++ * least 50ms. ++ */ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtrst = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ } ++ DWC_WORKQ_SCHEDULE_DELAYED(core_if->wq_otg, ++ hcd_start_func, dwc_otg_hcd, 50, ++ "start hcd"); ++ ++ return 1; ++} ++ ++/** ++ * HCD Callback function for disconnect of the HCD. ++ * ++ * @param p void pointer to the <code>struct usb_hcd</code> ++ */ ++static int32_t dwc_otg_hcd_disconnect_cb(void *p) ++{ ++ gintsts_data_t intr; ++ dwc_otg_hcd_t *dwc_otg_hcd = p; ++ ++ /* ++ * Set status flags for the hub driver. ++ */ ++ dwc_otg_hcd->flags.b.port_connect_status_change = 1; ++ dwc_otg_hcd->flags.b.port_connect_status = 0; ++ ++ /* ++ * Shutdown any transfers in process by clearing the Tx FIFO Empty ++ * interrupt mask and status bits and disabling subsequent host ++ * channel interrupts. ++ */ ++ intr.d32 = 0; ++ intr.b.nptxfempty = 1; ++ intr.b.ptxfempty = 1; ++ intr.b.hcintr = 1; ++ dwc_modify_reg32(&dwc_otg_hcd->core_if->core_global_regs->gintmsk, ++ intr.d32, 0); ++ dwc_modify_reg32(&dwc_otg_hcd->core_if->core_global_regs->gintsts, ++ intr.d32, 0); ++ ++ del_timers(dwc_otg_hcd); ++ ++ /* ++ * Turn off the vbus power only if the core has transitioned to device ++ * mode. If still in host mode, need to keep power on to detect a ++ * reconnection. ++ */ ++ if (dwc_otg_is_device_mode(dwc_otg_hcd->core_if)) { ++ if (dwc_otg_hcd->core_if->op_state != A_SUSPEND) { ++ hprt0_data_t hprt0 = {.d32 = 0 }; ++ DWC_PRINTF("Disconnect: PortPower off\n"); ++ hprt0.b.prtpwr = 0; ++ dwc_write_reg32(dwc_otg_hcd->core_if->host_if->hprt0, ++ hprt0.d32); ++ } ++ ++ dwc_otg_disable_host_interrupts(dwc_otg_hcd->core_if); ++ } ++ ++ /* Respond with an error status to all URBs in the schedule. */ ++ kill_all_urbs(dwc_otg_hcd); ++ ++ if (dwc_otg_is_host_mode(dwc_otg_hcd->core_if)) { ++ /* Clean up any host channels that were in use. */ ++ int num_channels; ++ int i; ++ dwc_hc_t *channel; ++ dwc_otg_hc_regs_t *hc_regs; ++ hcchar_data_t hcchar; ++ ++ num_channels = dwc_otg_hcd->core_if->core_params->host_channels; ++ ++ if (!dwc_otg_hcd->core_if->dma_enable) { ++ /* Flush out any channel requests in slave mode. */ ++ for (i = 0; i < num_channels; i++) { ++ channel = dwc_otg_hcd->hc_ptr_array[i]; ++ if (DWC_CIRCLEQ_EMPTY_ENTRY ++ (channel, hc_list_entry)) { ++ hc_regs = ++ dwc_otg_hcd->core_if->host_if-> ++ hc_regs[i]; ++ hcchar.d32 = ++ dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ hcchar.b.chen = 0; ++ hcchar.b.chdis = 1; ++ hcchar.b.epdir = 0; ++ dwc_write_reg32(&hc_regs-> ++ hcchar, ++ hcchar.d32); ++ } ++ } ++ } ++ } ++ ++ for (i = 0; i < num_channels; i++) { ++ channel = dwc_otg_hcd->hc_ptr_array[i]; ++ if (DWC_CIRCLEQ_EMPTY_ENTRY(channel, hc_list_entry)) { ++ hc_regs = ++ dwc_otg_hcd->core_if->host_if->hc_regs[i]; ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ /* Halt the channel. */ ++ hcchar.b.chdis = 1; ++ dwc_write_reg32(&hc_regs->hcchar, ++ hcchar.d32); ++ } ++ ++ dwc_otg_hc_cleanup(dwc_otg_hcd->core_if, ++ channel); ++ DWC_CIRCLEQ_INSERT_TAIL(&dwc_otg_hcd-> ++ free_hc_list, channel, ++ hc_list_entry); ++ /* ++ * Added for Descriptor DMA to prevent channel double cleanup ++ * in release_channel_ddma(). Which called from ep_disable ++ * when device disconnect. ++ */ ++ channel->qh = NULL; ++ } ++ } ++ } ++ ++ if (dwc_otg_hcd->fops->disconnect) { ++ dwc_otg_hcd->fops->disconnect(dwc_otg_hcd); ++ } ++ ++ return 1; ++} ++ ++/** ++ * HCD Callback function for stopping the HCD. ++ * ++ * @param p void pointer to the <code>struct usb_hcd</code> ++ */ ++static int32_t dwc_otg_hcd_stop_cb(void *p) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = p; ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p)\n", __func__, p); ++ dwc_otg_hcd_stop(dwc_otg_hcd); ++ return 1; ++} ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++/** ++ * HCD Callback function for sleep of HCD. ++ * ++ * @param p void pointer to the <code>struct usb_hcd</code> ++ */ ++static int dwc_otg_hcd_sleep_cb(void *p) ++{ ++ dwc_otg_hcd_t *hcd = p; ++ ++ dwc_otg_hcd_free_hc_from_lpm(hcd); ++ ++ return 0; ++} ++#endif ++ ++/** ++ * HCD Callback function for Remote Wakeup. ++ * ++ * @param p void pointer to the <code>struct usb_hcd</code> ++ */ ++static int dwc_otg_hcd_rem_wakeup_cb(void *p) ++{ ++ dwc_otg_hcd_t *hcd = p; ++ ++ if (hcd->core_if->lx_state == DWC_OTG_L2) { ++ hcd->flags.b.port_suspend_change = 1; ++ } ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ else { ++ hcd->flags.b.port_l1_change = 1; ++ } ++#endif ++ return 0; ++} ++ ++/** ++ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are ++ * stopped. ++ */ ++void dwc_otg_hcd_stop(dwc_otg_hcd_t * hcd) ++{ ++ hprt0_data_t hprt0 = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD STOP\n"); ++ ++ /* ++ * The root hub should be disconnected before this function is called. ++ * The disconnect will clear the QTD lists (via ..._hcd_urb_dequeue) ++ * and the QH lists (via ..._hcd_endpoint_disable). ++ */ ++ ++ /* Turn off all host-specific interrupts. */ ++ dwc_otg_disable_host_interrupts(hcd->core_if); ++ ++ /* Turn off the vbus power */ ++ DWC_PRINTF("PortPower off\n"); ++ hprt0.b.prtpwr = 0; ++ dwc_write_reg32(hcd->core_if->host_if->hprt0, hprt0.d32); ++ dwc_mdelay(1); ++} ++ ++int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * hcd, ++ dwc_otg_hcd_urb_t * dwc_otg_urb, void **ep_handle) ++{ ++ uint64_t flags; ++ int retval = 0; ++ dwc_otg_qtd_t *qtd; ++ ++ if (NULL == hcd->core_if) { //GRAYG ++ DWC_ERROR("**** DWC OTG HCD URB Enqueue - HCD has NULL core_if\n"); ++ /* No longer connected. */ ++ return -DWC_E_INVALID; ++ } ++ ++ if (!hcd->flags.b.port_connect_status) { ++ /* No longer connected. */ ++ return -DWC_E_NO_DEVICE; ++ } ++ ++ qtd = dwc_otg_hcd_qtd_create(dwc_otg_urb); ++ if (qtd == NULL) { ++ DWC_ERROR("DWC OTG HCD URB Enqueue failed creating QTD\n"); ++ return -DWC_E_NO_MEMORY; ++ } ++ if (qtd->urb == NULL) { //GRAYG ++ DWC_ERROR("**** DWC OTG HCD URB Enqueue created QTD with no URBs\n"); ++ return -DWC_E_NO_MEMORY; ++ } ++ if (qtd->urb->priv == NULL) { //GRAYG ++ DWC_ERROR("**** DWC OTG HCD URB Enqueue created QTD URB with no URB handle\n"); ++ return -DWC_E_NO_MEMORY; ++ } ++ ++ retval = ++ dwc_otg_hcd_qtd_add(qtd, hcd, (dwc_otg_qh_t **) ep_handle); ++ // creates a new queue in ep_handle if it doesn't exist already ++ if (retval < 0) { ++ DWC_ERROR("DWC OTG HCD URB Enqueue failed adding QTD. " ++ "Error status %d\n", retval); ++ dwc_otg_hcd_qtd_free(qtd); ++ } else { ++ qtd->qh = *ep_handle; ++ } ++ ++ if (hcd->core_if->dma_desc_enable && retval == 0) { ++ dwc_otg_transaction_type_e tr_type; ++ if ((qtd->qh->ep_type == UE_BULK) && !(qtd->urb->flags & URB_GIVEBACK_ASAP)) { ++ /* Do not schedule SG transcations until qtd has URB_GIVEBACK_ASAP set */ ++ return 0; ++ } ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ tr_type = dwc_otg_hcd_select_transactions(hcd); ++ if (tr_type != DWC_OTG_TRANSACTION_NONE) { ++ dwc_otg_hcd_queue_transactions(hcd, tr_type); ++ } ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ } ++ ++ return retval; ++} ++ ++int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * hcd, ++ dwc_otg_hcd_urb_t * dwc_otg_urb) ++{ ++ uint64_t flags; ++ ++ dwc_otg_qh_t *qh; ++ dwc_otg_qtd_t *urb_qtd; ++ ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ ++ if (hcd == NULL) { //GRAYG ++ DWC_ERROR("**** DWC OTG HCD URB Dequeue has NULL HCD\n"); ++ return -DWC_E_INVALID; ++ } ++ if (dwc_otg_urb == NULL) { //GRAYG ++ DWC_ERROR("**** DWC OTG HCD URB Dequeue has NULL URB\n"); ++ return -DWC_E_INVALID; ++ } ++ if (dwc_otg_urb->qtd == NULL) { //GRAYG ++ DWC_ERROR("**** DWC OTG HCD URB Dequeue with NULL QTD\n"); ++ return -DWC_E_INVALID; ++ } ++ urb_qtd = dwc_otg_urb->qtd; ++ if (urb_qtd->qh == NULL) { //GRAYG ++ DWC_ERROR("**** DWC OTG HCD URB Dequeue with QTD with NULL Q handler\n"); ++ return -DWC_E_INVALID; ++ } ++ qh = urb_qtd->qh; ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ if (urb_qtd->in_process) { ++ dump_channel_info(hcd, qh); ++ } ++ } ++#endif ++ if (hcd->core_if == NULL) { //GRAYG ++ DWC_ERROR("**** DWC OTG HCD URB Dequeue HCD has NULL core_if\n"); ++ return -DWC_E_INVALID; ++ } ++ if (urb_qtd->in_process && qh->channel) { ++ /* The QTD is in process (it has been assigned to a channel). */ ++ if (hcd->flags.b.port_connect_status) { ++ /* ++ * If still connected (i.e. in host mode), halt the ++ * channel so it can be used for other transfers. If ++ * no longer connected, the host registers can't be ++ * written to halt the channel since the core is in ++ * device mode. ++ */ ++ dwc_otg_hc_halt(hcd->core_if, qh->channel, ++ DWC_OTG_HC_XFER_URB_DEQUEUE); ++ } ++ } ++ ++ /* ++ * Free the QTD and clean up the associated QH. Leave the QH in the ++ * schedule if it has any remaining QTDs. ++ */ ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue - " ++ "delete %sQueue handler\n", ++ hcd->core_if->dma_desc_enable?"DMA ":""); //GRAYG ++ if (!hcd->core_if->dma_desc_enable) { ++ uint8_t b = urb_qtd->in_process; ++ dwc_otg_hcd_qtd_remove_and_free(hcd, urb_qtd, qh); ++ if (b) { ++ dwc_otg_hcd_qh_deactivate(hcd, qh, 0); ++ qh->channel = NULL; ++ } else if (DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ } ++ } ++ else { ++ dwc_otg_hcd_qtd_remove_and_free(hcd, urb_qtd, qh); ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ ++ return 0; ++} ++ ++int dwc_otg_hcd_endpoint_disable(dwc_otg_hcd_t * hcd, void *ep_handle, ++ int retry) ++{ ++ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle; ++ int retval = 0; ++ uint64_t flags; ++ ++ if (retry < 0) { ++ retval = -DWC_E_INVALID; ++ goto done; ++ } ++ ++ if (!qh) { ++ goto done; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ ++ while (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list) && retry) { ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ retry--; ++ dwc_msleep(5); ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ } ++ ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ /* ++ * Split dwc_otg_hcd_qh_remove_and_free() into qh_remove ++ * and qh_free to prevent stack dump on dwc_dma_free() with ++ * irq_disabled (spinlock_irqsave) in dwc_otg_hcd_desc_list_free() ++ * and dwc_otg_hcd_frame_list_alloc(). ++ */ ++ dwc_otg_hcd_qh_free(hcd, qh); ++ ++ done: ++ return retval; ++} ++ ++/** ++ * HCD Callback structure for handling mode switching. ++ */ ++static dwc_otg_cil_callbacks_t hcd_cil_callbacks = { ++ .start = dwc_otg_hcd_start_cb, ++ .stop = dwc_otg_hcd_stop_cb, ++ .disconnect = dwc_otg_hcd_disconnect_cb, ++ .session_start = dwc_otg_hcd_session_start_cb, ++ .resume_wakeup = dwc_otg_hcd_rem_wakeup_cb, ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ .sleep = dwc_otg_hcd_sleep_cb, ++#endif ++ .p = 0, ++}; ++ ++/** ++ * Reset tasklet function ++ */ ++static void reset_tasklet_func(void *data) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = (dwc_otg_hcd_t *) data; ++ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; ++ hprt0_data_t hprt0; ++ ++ DWC_DEBUGPL(DBG_HCDV, "USB RESET tasklet called\n"); ++ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtrst = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ dwc_mdelay(60); ++ ++ hprt0.b.prtrst = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ dwc_otg_hcd->flags.b.port_reset_change = 1; ++} ++ ++static void qh_list_free(dwc_otg_hcd_t * hcd, dwc_list_link_t * qh_list) ++{ ++ dwc_list_link_t *item; ++ dwc_otg_qh_t *qh; ++ ++ if (!qh_list->next) { ++ /* The list hasn't been initialized yet. */ ++ return; ++ } ++ ++ /* Ensure there are no QTDs or URBs left. */ ++ kill_urbs_in_qh_list(hcd, qh_list); ++ ++ DWC_LIST_FOREACH(item, qh_list) { ++ qh = DWC_LIST_ENTRY(item, dwc_otg_qh_t, qh_list_entry); ++ dwc_otg_hcd_qh_remove_and_free(hcd, qh); ++ } ++} ++ ++/** ++ * Frees secondary storage associated with the dwc_otg_hcd structure contained ++ * in the struct usb_hcd field. ++ */ ++static void dwc_otg_hcd_free(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ int i; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD FREE\n"); ++ ++ del_timers(dwc_otg_hcd); ++ ++ /* Free memory for QH/QTD lists */ ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_inactive); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->non_periodic_sched_active); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_inactive); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_ready); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_assigned); ++ qh_list_free(dwc_otg_hcd, &dwc_otg_hcd->periodic_sched_queued); ++ ++ /* Free memory for the host channels. */ ++ for (i = 0; i < MAX_EPS_CHANNELS; i++) { ++ dwc_hc_t *hc = dwc_otg_hcd->hc_ptr_array[i]; ++ ++#ifdef DEBUG ++ if (dwc_otg_hcd->core_if->hc_xfer_timer[i]) { ++ DWC_TIMER_FREE(dwc_otg_hcd->core_if->hc_xfer_timer[i]); ++ } ++#endif ++ if (hc != NULL) { ++ DWC_DEBUGPL(DBG_HCDV, "HCD Free channel #%i, hc=%p\n", ++ i, hc); ++ dwc_free(hc); ++ } ++ } ++ ++ if (dwc_otg_hcd->core_if->dma_enable) { ++ if (dwc_otg_hcd->status_buf_dma) { ++ dwc_dma_free(DWC_OTG_HCD_STATUS_BUF_SIZE, ++ dwc_otg_hcd->status_buf, ++ dwc_otg_hcd->status_buf_dma); ++ } ++ } else if (dwc_otg_hcd->status_buf != NULL) { ++ dwc_free(dwc_otg_hcd->status_buf); ++ } ++ DWC_SPINLOCK_FREE(dwc_otg_hcd->lock); ++ DWC_TIMER_FREE(dwc_otg_hcd->conn_timer); ++ DWC_TASK_FREE(dwc_otg_hcd->reset_tasklet); ++ dwc_free(dwc_otg_hcd); ++} ++ ++int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if) ++{ ++ int retval = 0; ++ int num_channels; ++ int i; ++ dwc_hc_t *channel; ++ ++ hcd->lock = DWC_SPINLOCK_ALLOC(); ++ ++ DWC_DEBUGPL(DBG_HCDV, "init of HCD %p given core_if %p\n", ++ hcd, core_if);//GRAYG ++ ++ hcd->core_if = core_if; ++ /* Register the HCD CIL Callbacks */ ++ dwc_otg_cil_register_hcd_callbacks(hcd->core_if, ++ &hcd_cil_callbacks, hcd); ++ ++ /* Initialize the non-periodic schedule. */ ++ DWC_LIST_INIT(&hcd->non_periodic_sched_inactive); ++ DWC_LIST_INIT(&hcd->non_periodic_sched_active); ++ ++ /* Initialize the periodic schedule. */ ++ DWC_LIST_INIT(&hcd->periodic_sched_inactive); ++ DWC_LIST_INIT(&hcd->periodic_sched_ready); ++ DWC_LIST_INIT(&hcd->periodic_sched_assigned); ++ DWC_LIST_INIT(&hcd->periodic_sched_queued); ++ ++ /* ++ * Create a host channel descriptor for each host channel implemented ++ * in the controller. Initialize the channel descriptor array. ++ */ ++ DWC_CIRCLEQ_INIT(&hcd->free_hc_list); ++ num_channels = hcd->core_if->core_params->host_channels; ++ DWC_MEMSET(hcd->hc_ptr_array, 0, sizeof(hcd->hc_ptr_array)); ++ for (i = 0; i < num_channels; i++) { ++ channel = dwc_alloc(sizeof(dwc_hc_t)); ++ if (channel == NULL) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR("%s: host channel allocation failed\n", ++ __func__); ++ dwc_otg_hcd_free(hcd); ++ goto out; ++ } ++ channel->hc_num = i; ++ hcd->hc_ptr_array[i] = channel; ++#ifdef DEBUG ++ hcd->core_if->hc_xfer_timer[i] = ++ DWC_TIMER_ALLOC("hc timer", hc_xfer_timeout, ++ &hcd->core_if->hc_xfer_info[i]); ++#endif ++ DWC_DEBUGPL(DBG_HCDV, "HCD Added channel #%d, hc=%p\n", i, ++ channel); ++ } ++ ++ /* Initialize the Connection timeout timer. */ ++ hcd->conn_timer = DWC_TIMER_ALLOC("Connection timer", ++ dwc_otg_hcd_connect_timeout, 0); ++ ++ /* Initialize reset tasklet. */ ++ hcd->reset_tasklet = DWC_TASK_ALLOC(reset_tasklet_func, hcd); ++ ++ /* ++ * Allocate space for storing data on status transactions. Normally no ++ * data is sent, but this space acts as a bit bucket. This must be ++ * done after usb_add_hcd since that function allocates the DMA buffer ++ * pool. ++ */ ++ if (hcd->core_if->dma_enable) { ++ hcd->status_buf = ++ dwc_dma_alloc(DWC_OTG_HCD_STATUS_BUF_SIZE, ++ &hcd->status_buf_dma); ++ } else { ++ hcd->status_buf = dwc_alloc(DWC_OTG_HCD_STATUS_BUF_SIZE); ++ } ++ if (!hcd->status_buf) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR("%s: status_buf allocation failed\n", __func__); ++ dwc_otg_hcd_free(hcd); ++ goto out; ++ } ++ ++ hcd->otg_port = 1; ++ hcd->frame_list = NULL; ++ hcd->frame_list_dma = 0; ++ ++#ifdef HW2937_WORKAROUND ++ hcd->hw2937_xfer_mode = HW2937_XFER_MODE_IDLE; ++ hcd->hw2937_assigned_channels = 0; ++#endif ++ ++out: ++ return retval; ++} ++ ++void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd) ++{ ++ /* Turn off all host-specific interrupts. */ ++ dwc_otg_disable_host_interrupts(hcd->core_if); ++ ++ dwc_otg_hcd_free(hcd); ++} ++ ++/** ++ * Initializes dynamic portions of the DWC_otg HCD state. ++ */ ++static void dwc_otg_hcd_reinit(dwc_otg_hcd_t * hcd) ++{ ++ int num_channels; ++ int i; ++ dwc_hc_t *channel; ++ dwc_hc_t *channel_tmp; ++ ++ hcd->flags.d32 = 0; ++ ++ hcd->non_periodic_qh_ptr = &hcd->non_periodic_sched_active; ++ hcd->non_periodic_channels = 0; ++ hcd->periodic_channels = 0; ++ ++ /* ++ * Put all channels in the free channel list and clean up channel ++ * states. ++ */ ++ DWC_CIRCLEQ_FOREACH_SAFE(channel, channel_tmp, ++ &hcd->free_hc_list, hc_list_entry) { ++ DWC_CIRCLEQ_REMOVE(&hcd->free_hc_list, channel, hc_list_entry); ++ } ++ ++ num_channels = hcd->core_if->core_params->host_channels; ++ for (i = 0; i < num_channels; i++) { ++ channel = hcd->hc_ptr_array[i]; ++ DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, channel, ++ hc_list_entry); ++ dwc_otg_hc_cleanup(hcd->core_if, channel); ++ } ++ ++ /* Initialize the DWC core for host mode operation. */ ++ dwc_otg_core_host_init(hcd->core_if); ++} ++ ++/** ++ * Assigns transactions from a QTD to a free host channel and initializes the ++ * host channel to perform the transactions. The host channel is removed from ++ * the free list. ++ * ++ * @param hcd The HCD state structure. ++ * @param qh Transactions from the first QTD for this QH are selected and ++ * assigned to a free host channel. ++ */ ++#ifdef HW2937_WORKAROUND ++static int assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++#else ++static void assign_and_init_hc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++#endif ++{ ++ dwc_hc_t *hc; ++ dwc_otg_qtd_t *qtd; ++ dwc_otg_hcd_urb_t *urb; ++ void* ptr = NULL; ++#ifdef HW2937_WORKAROUND ++ int ep_is_in; ++#endif ++ ++ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ ++ urb = qtd->urb; ++ ++ DWC_DEBUGPL(DBG_HCDV, "%s(%p,%p) - urb %x, actual_length %d\n", __func__, hcd, qh, (unsigned int)urb, urb->actual_length); ++ ++#ifdef HW2937_WORKAROUND ++ ep_is_in = (dwc_otg_hcd_is_pipe_in(&urb->pipe_info) != 0); ++ if (ep_is_in && ((hcd->hw2937_xfer_mode == HW2937_XFER_MODE_OUT) || ++ (hcd->hw2937_xfer_mode == HW2937_XFER_MODE_PAUSEIN))) ++ return 0; ++#endif ++ ++ hc = DWC_CIRCLEQ_FIRST(&hcd->free_hc_list); ++ ++ /* Remove the host channel from the free list. */ ++ DWC_CIRCLEQ_REMOVE_INIT(&hcd->free_hc_list, hc, hc_list_entry); ++ qh->channel = hc; ++ ++ qtd->in_process = 1; ++ ++ /* ++ * Use usb_pipedevice to determine device address. This address is ++ * 0 before the SET_ADDRESS command and the correct address afterward. ++ */ ++ hc->dev_addr = dwc_otg_hcd_get_dev_addr(&urb->pipe_info); ++ hc->ep_num = dwc_otg_hcd_get_ep_num(&urb->pipe_info); ++ hc->speed = qh->dev_speed; ++ hc->max_packet = dwc_max_packet(qh->maxp); ++ ++ hc->xfer_started = 0; ++ hc->halt_status = DWC_OTG_HC_XFER_NO_HALT_STATUS; ++ hc->error_state = (qtd->error_count > 0); ++ hc->halt_on_queue = 0; ++ hc->halt_pending = 0; ++ hc->requests = 0; ++ ++ /* ++ * The following values may be modified in the transfer type section ++ * below. The xfer_len value may be reduced when the transfer is ++ * started to accommodate the max widths of the XferSize and PktCnt ++ * fields in the HCTSIZn register. ++ */ ++ hc->do_ping = qh->ping_state; ++#ifdef HW2937_WORKAROUND ++ hc->ep_is_in = ep_is_in; ++#else ++ hc->ep_is_in = (dwc_otg_hcd_is_pipe_in(&urb->pipe_info) != 0); ++#endif ++ hc->data_pid_start = qh->data_toggle; ++ hc->multi_count = 1; ++ ++ if (hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *) urb->dma + urb->actual_length; ++ ++ /* For non-dword aligned case */ ++ if (((uint32_t)hc->xfer_buff & 0x3) && !hcd->core_if->dma_desc_enable) { ++ ptr = (uint8_t *) urb->buf + urb->actual_length; ++ } ++ } else { ++ hc->xfer_buff = (uint8_t *) urb->buf + urb->actual_length; ++ } ++ hc->xfer_len = urb->length - urb->actual_length; ++ hc->xfer_count = 0; ++ ++ /* ++ * Set the split attributes ++ */ ++ hc->do_split = 0; ++ if (qh->do_split) { ++ uint32_t hub_addr, port_addr; ++ hc->do_split = 1; ++ hc->xact_pos = qtd->isoc_split_pos; ++ hc->complete_split = qtd->complete_split; ++ hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &port_addr); ++ hc->hub_addr = (uint8_t) hub_addr; ++ hc->port_addr = (uint8_t) port_addr; ++ } ++ ++ switch (dwc_otg_hcd_get_pipe_type(&urb->pipe_info)) { ++ case UE_CONTROL: ++ hc->ep_type = DWC_OTG_EP_TYPE_CONTROL; ++ switch (qtd->control_phase) { ++ case DWC_OTG_CONTROL_SETUP: ++ DWC_DEBUGPL(DBG_HCDV, " Control setup transaction\n"); ++ hc->do_ping = 0; ++ hc->ep_is_in = 0; ++ hc->data_pid_start = DWC_OTG_HC_PID_SETUP; ++ if (hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *) urb->setup_dma; ++ } else { ++ hc->xfer_buff = (uint8_t *) urb->setup_packet; ++ } ++ hc->xfer_len = 8; ++ ptr = NULL; ++ break; ++ case DWC_OTG_CONTROL_DATA: ++ DWC_DEBUGPL(DBG_HCDV, " Control data transaction\n"); ++ hc->data_pid_start = qtd->data_toggle; ++ break; ++ case DWC_OTG_CONTROL_STATUS: ++ /* ++ * Direction is opposite of data direction or IN if no ++ * data. ++ */ ++ DWC_DEBUGPL(DBG_HCDV, " Control status transaction\n"); ++ if (urb->length == 0) { ++ hc->ep_is_in = 1; ++ } else { ++ hc->ep_is_in = ++ dwc_otg_hcd_is_pipe_out(&urb->pipe_info); ++ } ++ if (hc->ep_is_in) { ++ hc->do_ping = 0; ++ } ++ ++ hc->data_pid_start = DWC_OTG_HC_PID_DATA1; ++ ++ hc->xfer_len = 0; ++ if (hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *) hcd->status_buf_dma; ++ } else { ++ hc->xfer_buff = (uint8_t *) hcd->status_buf; ++ } ++ ptr = NULL; ++ break; ++ } ++ break; ++ case UE_BULK: ++ hc->ep_type = DWC_OTG_EP_TYPE_BULK; ++ break; ++ case UE_INTERRUPT: ++ hc->ep_type = DWC_OTG_EP_TYPE_INTR; ++ break; ++ case UE_ISOCHRONOUS: ++ { ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc; ++ ++ hc->ep_type = DWC_OTG_EP_TYPE_ISOC; ++ ++ if (hcd->core_if->dma_desc_enable) ++ break; ++ ++ frame_desc = &urb->iso_descs[qtd->isoc_frame_index]; ++ ++ frame_desc->status = 0; ++ ++ if (hcd->core_if->dma_enable) { ++ hc->xfer_buff = (uint8_t *) urb->dma; ++ } else { ++ hc->xfer_buff = (uint8_t *) urb->buf; ++ } ++ hc->xfer_buff += ++ frame_desc->offset + qtd->isoc_split_offset; ++ hc->xfer_len = ++ frame_desc->length - qtd->isoc_split_offset; ++ ++ /* For non-dword aligned buffers */ ++ if (((uint32_t)hc->xfer_buff & 0x3) && hcd->core_if->dma_enable) { ++ ptr = (uint8_t *) urb->buf + frame_desc->offset + qtd->isoc_split_offset; ++ } ++ else ++ ptr = NULL; ++ ++ if (hc->xact_pos == DWC_HCSPLIT_XACTPOS_ALL) { ++ if (hc->xfer_len <= 188) { ++ hc->xact_pos = DWC_HCSPLIT_XACTPOS_ALL; ++ } else { ++ hc->xact_pos = ++ DWC_HCSPLIT_XACTPOS_BEGIN; ++ } ++ } ++ } ++ break; ++ } ++ /* non DWORD-aligned buffer case */ ++ if (ptr) { ++ uint32_t buf_size; ++ if (hc->ep_type != DWC_OTG_EP_TYPE_ISOC) { ++ buf_size = hcd->core_if->core_params->max_transfer_size; ++ } else { ++ buf_size = 4096; ++ } ++ if (!qh->dw_align_buf) { ++ qh->dw_align_buf = ++ dwc_dma_alloc_atomic(buf_size, ++ &qh->dw_align_buf_dma); ++ if (!qh->dw_align_buf) { ++ DWC_ERROR("%s: Failed to allocate memory to handle " ++ "non-dword aligned buffer case\n", __func__); ++#ifdef HW2937_WORKAROUND ++ return 0; ++#else ++ return; ++#endif ++ } ++ } ++ if (!hc->ep_is_in) { ++ dwc_memcpy(qh->dw_align_buf, ptr, hc->xfer_len); ++ } ++ hc->align_buff = qh->dw_align_buf_dma; ++ } ++ else { ++ hc->align_buff = 0; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * This value may be modified when the transfer is started to ++ * reflect the actual transfer length. ++ */ ++ hc->multi_count = dwc_hb_mult(qh->maxp); ++ } ++ ++ if (hcd->core_if->dma_desc_enable) ++ hc->desc_list_addr = qh->desc_list_dma; ++ ++ dwc_otg_hc_init(hcd->core_if, hc); ++ hc->qh = qh; ++#ifdef HW2937_WORKAROUND ++ hcd->hw2937_assigned_channels |= (1 << hc->hc_num); ++ DWC_DEBUGPL(DBG_HW2937, " assign %d -> hw2937_ac %x\n", hc->hc_num, hcd->hw2937_assigned_channels); ++ return 1; ++#endif ++} ++ ++#ifdef HW2937_WORKAROUND ++ ++void debug_halt(void) ++{ ++ spinlock_t mr_lock = SPIN_LOCK_UNLOCKED; ++ unsigned long flags; ++ extern void v6_flush_kern_cache_all(void); ++ ++ spin_lock_irqsave(&mr_lock, flags); ++#ifdef CONFIG_MACH_BCM2708 ++ v6_flush_kern_cache_all(); ++#endif ++ while (1) continue; ++} ++ ++static ++void dwc_otg_hcd_disable_in_channels(dwc_otg_hcd_t * hcd) ++{ ++ int num_channels = hcd->core_if->core_params->host_channels; ++ static int stall_count = 0; ++ static int max_stall_count = 1; ++ static int last_stalled = 0; ++ int stalled = 0; ++ int i; ++ ++ DWC_DEBUGPL(DBG_HW2937, " Disable In Channels(%x)\n", hcd->hw2937_assigned_channels); ++ ++ for (i = 0; i < num_channels; i++) { ++ if (hcd->hw2937_assigned_channels & (1 << i)) { ++ dwc_hc_t *hc = hcd->hc_ptr_array[i]; ++ if (!hc->halt_pending) { ++ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num]; ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ DWC_DEBUGPL(DBG_HW2937, "pktcnt %d, xfersize %x, xfer_len %x\n", hctsiz.b.pktcnt, hctsiz.b.xfersize, hc->xfer_len); ++ if (hctsiz.b.pktcnt == hc->start_pkt_count) ++ { ++ dwc_otg_hc_halt(hcd->core_if, hc, DWC_OTG_HC_XFER_PAUSE_IN); ++ } ++ else ++ { ++ /* Unless a receive is in progress */ ++ stalled |= (1<<i); ++ } ++ } else { ++ stalled |= (1<<i); ++ } ++ } ++ } ++ ++ if (stalled && (stalled == last_stalled)) ++ { ++ stall_count++; ++ if (stall_count > max_stall_count) ++ { ++ max_stall_count = stall_count; ++ DWC_PRINTF( "stall (%x) count -> %d\n", stalled, stall_count); ++ if (stall_count == 10) ++ { ++ debug_halt(); ++ } ++ } ++ } ++ else ++ { ++ stall_count = 0; ++ last_stalled = stalled; ++ } ++} ++ ++static ++int dwc_otg_hcd_update_transaction_mode(dwc_otg_hcd_t * hcd) ++{ ++ dwc_list_link_t *qh_ptr; ++ dwc_otg_qh_t *qh; ++ dwc_otg_qtd_t *qtd; ++ dwc_otg_hcd_urb_t *urb; ++ int found_in = 0; ++ ++ /* If there are any existing out transactions, stay in OUT mode */ ++ if (hcd->hw2937_xfer_mode == HW2937_XFER_MODE_OUT) ++ { ++ return 1; ++ } ++ ++ /* Scan entries in the periodic ready list. */ ++ qh_ptr = DWC_LIST_FIRST(&hcd->periodic_sched_ready); ++ ++ while (qh_ptr != &hcd->periodic_sched_ready) { ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ urb = qtd->urb; ++ if (!dwc_otg_hcd_is_pipe_in(&urb->pipe_info)) { ++ /* Switch to OUT mode */ ++ switch (hcd->hw2937_xfer_mode) ++ { ++ case HW2937_XFER_MODE_IDLE: ++ DWC_DEBUGPL(DBG_HW2937, "utm -> OUT\n"); ++ hcd->hw2937_xfer_mode = HW2937_XFER_MODE_OUT; ++ /* Drop through... */ ++ case HW2937_XFER_MODE_OUT: ++ return 1; ++ case HW2937_XFER_MODE_IN: ++ DWC_DEBUGPL(DBG_HW2937, "utm - halting %x INs\n", hcd->hw2937_assigned_channels); ++ /* Disable the channels with outstanding INs */ ++ dwc_otg_hcd_disable_in_channels(hcd); ++ ++ DWC_DEBUGPL(DBG_HW2937, "utm -> PAUSEIN\n"); ++ hcd->hw2937_xfer_mode = HW2937_XFER_MODE_PAUSEIN; ++ /* Drop through... */ ++ case HW2937_XFER_MODE_PAUSEIN: ++ /* Delay until the halt completes */ ++ return 0; ++ } ++ } ++ found_in = 1; ++ qh_ptr = DWC_LIST_NEXT(qh_ptr); ++ } ++ ++ /* ++ * Scan entries in the inactive portion of the non-periodic ++ * schedule. ++ */ ++ qh_ptr = hcd->non_periodic_sched_inactive.next; ++ while (qh_ptr != &hcd->non_periodic_sched_inactive) { ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ urb = qtd->urb; ++ if (!dwc_otg_hcd_is_pipe_in(&urb->pipe_info)) { ++ /* Switch to OUT mode */ ++ switch (hcd->hw2937_xfer_mode) ++ { ++ case HW2937_XFER_MODE_IDLE: ++ DWC_DEBUGPL(DBG_HW2937, "utm -> OUT\n"); ++ hcd->hw2937_xfer_mode = HW2937_XFER_MODE_OUT; ++ /* Drop through... */ ++ case HW2937_XFER_MODE_OUT: ++ return 1; ++ case HW2937_XFER_MODE_IN: ++ DWC_DEBUGPL(DBG_HW2937, "utm - halting %x INs\n", hcd->hw2937_assigned_channels); ++ /* Disable the channels with outstanding INs */ ++ dwc_otg_hcd_disable_in_channels(hcd); ++ ++ DWC_DEBUGPL(DBG_HW2937, "utm -> PAUSEIN\n"); ++ hcd->hw2937_xfer_mode = HW2937_XFER_MODE_PAUSEIN; ++ /* Drop through... */ ++ case HW2937_XFER_MODE_PAUSEIN: ++ /* Delay until the halt completes */ ++ return 0; ++ } ++ } ++ found_in = 1; ++ qh_ptr = DWC_LIST_NEXT(qh_ptr); ++ } ++ ++ if (found_in && (hcd->hw2937_xfer_mode == HW2937_XFER_MODE_IDLE)) ++ { ++ DWC_DEBUGPL(DBG_HW2937, "utm -> IN\n"); ++ hcd->hw2937_xfer_mode = HW2937_XFER_MODE_IN; ++ } ++ return 1; ++} ++ ++#endif /* HW2937_WORKAROUND */ ++ ++/** ++ * This function selects transactions from the HCD transfer schedule and ++ * assigns them to available host channels. It is called from HCD interrupt ++ * handler functions. ++ * ++ * @param hcd The HCD state structure. ++ * ++ * @return The types of new transactions that were assigned to host channels. ++ */ ++dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t * hcd) ++{ ++ dwc_list_link_t *qh_ptr; ++ dwc_otg_qh_t *qh; ++ int num_channels; ++ dwc_otg_transaction_type_e ret_val = DWC_OTG_TRANSACTION_NONE; ++ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCD, " Select Transactions\n"); ++#endif ++ ++#ifdef HW2937_WORKAROUND ++ if (!dwc_otg_hcd_update_transaction_mode(hcd)) ++ { ++ return ret_val; ++ } ++#endif ++ ++ /* Process entries in the periodic ready list. */ ++ qh_ptr = DWC_LIST_FIRST(&hcd->periodic_sched_ready); ++ ++ while (qh_ptr != &hcd->periodic_sched_ready && ++ !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++#ifdef HW2937_WORKAROUND ++ if (assign_and_init_hc(hcd, qh)) { ++#else ++ assign_and_init_hc(hcd, qh); ++#endif ++ ++ /* ++ * Move the QH from the periodic ready schedule to the ++ * periodic assigned schedule. ++ */ ++ qh_ptr = DWC_LIST_NEXT(qh_ptr); ++ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned, ++ &qh->qh_list_entry); ++ ++ ret_val = DWC_OTG_TRANSACTION_PERIODIC; ++#ifdef HW2937_WORKAROUND ++ } else { ++ qh_ptr = DWC_LIST_NEXT(qh_ptr); ++ } ++#endif ++ } ++ ++ /* ++ * Process entries in the inactive portion of the non-periodic ++ * schedule. Some free host channels may not be used if they are ++ * reserved for periodic transfers. ++ */ ++ qh_ptr = hcd->non_periodic_sched_inactive.next; ++ num_channels = hcd->core_if->core_params->host_channels; ++ while (qh_ptr != &hcd->non_periodic_sched_inactive && ++ (hcd->non_periodic_channels < ++ num_channels - hcd->periodic_channels) && ++ !DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { ++ ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ ++#ifdef HW2937_WORKAROUND ++ if (assign_and_init_hc(hcd, qh)) { ++#else ++ assign_and_init_hc(hcd, qh); ++#endif ++ ++ /* ++ * Move the QH from the non-periodic inactive schedule to the ++ * non-periodic active schedule. ++ */ ++ qh_ptr = DWC_LIST_NEXT(qh_ptr); ++ DWC_LIST_MOVE_HEAD(&hcd->non_periodic_sched_active, ++ &qh->qh_list_entry); ++ ++ if (ret_val == DWC_OTG_TRANSACTION_NONE) { ++ ret_val = DWC_OTG_TRANSACTION_NON_PERIODIC; ++ } else { ++ ret_val = DWC_OTG_TRANSACTION_ALL; ++ } ++ ++ hcd->non_periodic_channels++; ++#ifdef HW2937_WORKAROUND ++ } else { ++ qh_ptr = DWC_LIST_NEXT(qh_ptr); ++ } ++#endif ++ } ++ ++ return ret_val; ++} ++/** ++ * Attempts to queue a single transaction request for a host channel ++ * associated with either a periodic or non-periodic transfer. This function ++ * assumes that there is space available in the appropriate request queue. For ++ * an OUT transfer or SETUP transaction in Slave mode, it checks whether space ++ * is available in the appropriate Tx FIFO. ++ * ++ * @param hcd The HCD state structure. ++ * @param hc Host channel descriptor associated with either a periodic or ++ * non-periodic transfer. ++ * @param fifo_dwords_avail Number of DWORDs available in the periodic Tx ++ * FIFO for periodic transfers or the non-periodic Tx FIFO for non-periodic ++ * transfers. ++ * ++ * @return 1 if a request is queued and more requests may be needed to ++ * complete the transfer, 0 if no more requests are required for this ++ * transfer, -1 if there is insufficient space in the Tx FIFO. ++ */ ++static int queue_transaction(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, uint16_t fifo_dwords_avail) ++{ ++ int retval; ++ ++ if (hcd->core_if->dma_enable) { ++ if (hcd->core_if->dma_desc_enable) { ++ if (!hc->xfer_started || (hc->ep_type == DWC_OTG_EP_TYPE_ISOC)) { ++ dwc_otg_hcd_start_xfer_ddma(hcd, hc->qh); ++ hc->qh->ping_state = 0; ++ } ++ } ++ else if (!hc->xfer_started) { ++ dwc_otg_hc_start_transfer(hcd->core_if, hc); ++ hc->qh->ping_state = 0; ++ } ++ retval = 0; ++ } else if (hc->halt_pending) { ++ /* Don't queue a request if the channel has been halted. */ ++ retval = 0; ++ } else if (hc->halt_on_queue) { ++ dwc_otg_hc_halt(hcd->core_if, hc, hc->halt_status); ++ retval = 0; ++ } else if (hc->do_ping) { ++ if (!hc->xfer_started) { ++ dwc_otg_hc_start_transfer(hcd->core_if, hc); ++ } ++ retval = 0; ++ } else if (!hc->ep_is_in || hc->data_pid_start == DWC_OTG_HC_PID_SETUP) { ++ if ((fifo_dwords_avail * 4) >= hc->max_packet) { ++ if (!hc->xfer_started) { ++ dwc_otg_hc_start_transfer(hcd->core_if, hc); ++ retval = 1; ++ } else { ++ retval = ++ dwc_otg_hc_continue_transfer(hcd->core_if, ++ hc); ++ } ++ } else { ++ retval = -1; ++ } ++ } else { ++ if (!hc->xfer_started) { ++ dwc_otg_hc_start_transfer(hcd->core_if, hc); ++ retval = 1; ++ } else { ++ retval = dwc_otg_hc_continue_transfer(hcd->core_if, hc); ++ } ++ } ++ ++ return retval; ++} ++ ++/** ++ * Processes periodic channels for the next frame and queues transactions for ++ * these channels to the DWC_otg controller. After queueing transactions, the ++ * Periodic Tx FIFO Empty interrupt is enabled if there are more transactions ++ * to queue as Periodic Tx FIFO or request queue space becomes available. ++ * Otherwise, the Periodic Tx FIFO Empty interrupt is disabled. ++ */ ++static void process_periodic_channels(dwc_otg_hcd_t * hcd) ++{ ++ hptxsts_data_t tx_status; ++ dwc_list_link_t *qh_ptr; ++ dwc_otg_qh_t *qh; ++ int status; ++ int no_queue_space = 0; ++ int no_fifo_space = 0; ++ ++ dwc_otg_host_global_regs_t *host_regs; ++ host_regs = hcd->core_if->host_if->host_global_regs; ++ ++ DWC_DEBUGPL(DBG_HCDV, "Queue periodic transactions\n"); ++#ifdef DEBUG ++ tx_status.d32 = dwc_read_reg32(&host_regs->hptxsts); ++ DWC_DEBUGPL(DBG_HCDV, ++ " P Tx Req Queue Space Avail (before queue): %d\n", ++ tx_status.b.ptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, " P Tx FIFO Space Avail (before queue): %d\n", ++ tx_status.b.ptxfspcavail); ++#endif ++ ++ qh_ptr = hcd->periodic_sched_assigned.next; ++ while (qh_ptr != &hcd->periodic_sched_assigned) { ++ tx_status.d32 = dwc_read_reg32(&host_regs->hptxsts); ++ if (tx_status.b.ptxqspcavail == 0) { ++ no_queue_space = 1; ++ break; ++ } ++ ++ qh = DWC_LIST_ENTRY(qh_ptr, dwc_otg_qh_t, qh_list_entry); ++ ++ /* ++ * Set a flag if we're queuing high-bandwidth in slave mode. ++ * The flag prevents any halts to get into the request queue in ++ * the middle of multiple high-bandwidth packets getting queued. ++ */ ++ if (!hcd->core_if->dma_enable && qh->channel->multi_count > 1) { ++ hcd->core_if->queuing_high_bandwidth = 1; ++ } ++ status = ++ queue_transaction(hcd, qh->channel, ++ tx_status.b.ptxfspcavail); ++ if (status < 0) { ++ no_fifo_space = 1; ++ break; ++ } ++ ++ /* ++ * In Slave mode, stay on the current transfer until there is ++ * nothing more to do or the high-bandwidth request count is ++ * reached. In DMA mode, only need to queue one request. The ++ * controller automatically handles multiple packets for ++ * high-bandwidth transfers. ++ */ ++ if (hcd->core_if->dma_enable || status == 0 || ++ qh->channel->requests == qh->channel->multi_count) { ++ qh_ptr = qh_ptr->next; ++ /* ++ * Move the QH from the periodic assigned schedule to ++ * the periodic queued schedule. ++ */ ++ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_queued, ++ &qh->qh_list_entry); ++ ++ /* done queuing high bandwidth */ ++ hcd->core_if->queuing_high_bandwidth = 0; ++ } ++ } ++ ++ if (!hcd->core_if->dma_enable) { ++ dwc_otg_core_global_regs_t *global_regs; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ global_regs = hcd->core_if->core_global_regs; ++ intr_mask.b.ptxfempty = 1; ++#ifdef DEBUG ++ tx_status.d32 = dwc_read_reg32(&host_regs->hptxsts); ++ DWC_DEBUGPL(DBG_HCDV, ++ " P Tx Req Queue Space Avail (after queue): %d\n", ++ tx_status.b.ptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, ++ " P Tx FIFO Space Avail (after queue): %d\n", ++ tx_status.b.ptxfspcavail); ++#endif ++ if (!DWC_LIST_EMPTY(&hcd->periodic_sched_assigned) || ++ no_queue_space || no_fifo_space) { ++ /* ++ * May need to queue more transactions as the request ++ * queue or Tx FIFO empties. Enable the periodic Tx ++ * FIFO empty interrupt. (Always use the half-empty ++ * level to ensure that new requests are loaded as ++ * soon as possible.) ++ */ ++ dwc_modify_reg32(&global_regs->gintmsk, 0, ++ intr_mask.d32); ++ } else { ++ /* ++ * Disable the Tx FIFO empty interrupt since there are ++ * no more transactions that need to be queued right ++ * now. This function is called from interrupt ++ * handlers to queue more transactions as transfer ++ * states change. ++ */ ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, ++ 0); ++ } ++ } ++} ++ ++/** ++ * Processes active non-periodic channels and queues transactions for these ++ * channels to the DWC_otg controller. After queueing transactions, the NP Tx ++ * FIFO Empty interrupt is enabled if there are more transactions to queue as ++ * NP Tx FIFO or request queue space becomes available. Otherwise, the NP Tx ++ * FIFO Empty interrupt is disabled. ++ */ ++static void process_non_periodic_channels(dwc_otg_hcd_t * hcd) ++{ ++ gnptxsts_data_t tx_status; ++ dwc_list_link_t *orig_qh_ptr; ++ dwc_otg_qh_t *qh; ++ int status; ++ int no_queue_space = 0; ++ int no_fifo_space = 0; ++ int more_to_do = 0; ++ ++ dwc_otg_core_global_regs_t *global_regs = ++ hcd->core_if->core_global_regs; ++ ++ DWC_DEBUGPL(DBG_HCDV, "Queue non-periodic transactions\n"); ++#ifdef DEBUG ++ tx_status.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_HCDV, ++ " NP Tx Req Queue Space Avail (before queue): %d\n", ++ tx_status.b.nptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, " NP Tx FIFO Space Avail (before queue): %d\n", ++ tx_status.b.nptxfspcavail); ++#endif ++ /* ++ * Keep track of the starting point. Skip over the start-of-list ++ * entry. ++ */ ++ if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) { ++ hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next; ++ } ++ orig_qh_ptr = hcd->non_periodic_qh_ptr; ++ ++ /* ++ * Process once through the active list or until no more space is ++ * available in the request queue or the Tx FIFO. ++ */ ++ do { ++ tx_status.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ if (!hcd->core_if->dma_enable && tx_status.b.nptxqspcavail == 0) { ++ no_queue_space = 1; ++ break; ++ } ++ ++ qh = DWC_LIST_ENTRY(hcd->non_periodic_qh_ptr, dwc_otg_qh_t, ++ qh_list_entry); ++ status = ++ queue_transaction(hcd, qh->channel, ++ tx_status.b.nptxfspcavail); ++ ++ if (status > 0) { ++ more_to_do = 1; ++ } else if (status < 0) { ++ no_fifo_space = 1; ++ break; ++ } ++ ++ /* Advance to next QH, skipping start-of-list entry. */ ++ hcd->non_periodic_qh_ptr = hcd->non_periodic_qh_ptr->next; ++ if (hcd->non_periodic_qh_ptr == &hcd->non_periodic_sched_active) { ++ hcd->non_periodic_qh_ptr = ++ hcd->non_periodic_qh_ptr->next; ++ } ++ ++ } while (hcd->non_periodic_qh_ptr != orig_qh_ptr); ++ ++ if (!hcd->core_if->dma_enable) { ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ intr_mask.b.nptxfempty = 1; ++ ++#ifdef DEBUG ++ tx_status.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_HCDV, ++ " NP Tx Req Queue Space Avail (after queue): %d\n", ++ tx_status.b.nptxqspcavail); ++ DWC_DEBUGPL(DBG_HCDV, ++ " NP Tx FIFO Space Avail (after queue): %d\n", ++ tx_status.b.nptxfspcavail); ++#endif ++ if (more_to_do || no_queue_space || no_fifo_space) { ++ /* ++ * May need to queue more transactions as the request ++ * queue or Tx FIFO empties. Enable the non-periodic ++ * Tx FIFO empty interrupt. (Always use the half-empty ++ * level to ensure that new requests are loaded as ++ * soon as possible.) ++ */ ++ dwc_modify_reg32(&global_regs->gintmsk, 0, ++ intr_mask.d32); ++ } else { ++ /* ++ * Disable the Tx FIFO empty interrupt since there are ++ * no more transactions that need to be queued right ++ * now. This function is called from interrupt ++ * handlers to queue more transactions as transfer ++ * states change. ++ */ ++ dwc_modify_reg32(&global_regs->gintmsk, intr_mask.d32, ++ 0); ++ } ++ } ++} ++ ++/** ++ * This function processes the currently active host channels and queues ++ * transactions for these channels to the DWC_otg controller. It is called ++ * from HCD interrupt handler functions. ++ * ++ * @param hcd The HCD state structure. ++ * @param tr_type The type(s) of transactions to queue (non-periodic, ++ * periodic, or both). ++ */ ++void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd, ++ dwc_otg_transaction_type_e tr_type) ++{ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCD, "Queue Transactions\n"); ++#endif ++ /* Process host channels associated with periodic transfers. */ ++ if ((tr_type == DWC_OTG_TRANSACTION_PERIODIC || ++ tr_type == DWC_OTG_TRANSACTION_ALL) && ++ !DWC_LIST_EMPTY(&hcd->periodic_sched_assigned)) { ++ ++ process_periodic_channels(hcd); ++ } ++ ++ /* Process host channels associated with non-periodic transfers. */ ++ if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC || ++ tr_type == DWC_OTG_TRANSACTION_ALL) { ++ if (!DWC_LIST_EMPTY(&hcd->non_periodic_sched_active)) { ++ process_non_periodic_channels(hcd); ++ } else { ++ /* ++ * Ensure NP Tx FIFO empty interrupt is disabled when ++ * there are no non-periodic transfers to process. ++ */ ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ gintmsk.b.nptxfempty = 1; ++ dwc_modify_reg32(&hcd->core_if->core_global_regs-> ++ gintmsk, gintmsk.d32, 0); ++ } ++ } ++} ++ ++#ifdef DWC_HS_ELECT_TST ++/* ++ * Quick and dirty hack to implement the HS Electrical Test ++ * SINGLE_STEP_GET_DEVICE_DESCRIPTOR feature. ++ * ++ * This code was copied from our userspace app "hset". It sends a ++ * Get Device Descriptor control sequence in two parts, first the ++ * Setup packet by itself, followed some time later by the In and ++ * Ack packets. Rather than trying to figure out how to add this ++ * functionality to the normal driver code, we just hijack the ++ * hardware, using these two function to drive the hardware ++ * directly. ++ */ ++ ++static dwc_otg_core_global_regs_t *global_regs; ++static dwc_otg_host_global_regs_t *hc_global_regs; ++static dwc_otg_hc_regs_t *hc_regs; ++static uint32_t *data_fifo; ++ ++static void do_setup(void) ++{ ++ gintsts_data_t gintsts; ++ hctsiz_data_t hctsiz; ++ hcchar_data_t hcchar; ++ haint_data_t haint; ++ hcint_data_t hcint; ++ ++ /* Enable HAINTs */ ++ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0001); ++ ++ /* Enable HCINTs */ ++ dwc_write_reg32(&hc_regs->hcintmsk, 0x04a3); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++ /* ++ * Send Setup packet (Get Device Descriptor) ++ */ ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ hcchar.b.chdis = 1; ++// hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ dwc_mdelay(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 8; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = DWC_OTG_HC_PID_SETUP; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.epdir = 0; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ /* Fill FIFO with Setup data for Get Device Descriptor */ ++ data_fifo = (uint32_t *) ((char *)global_regs + 0x1000); ++ dwc_write_reg32(data_fifo++, 0x01000680); ++ dwc_write_reg32(data_fifo++, 0x00080000); ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ ++ /* Disable HCINTs */ ++ dwc_write_reg32(&hc_regs->hcintmsk, 0x0000); ++ ++ /* Disable HAINTs */ ++ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0000); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++} ++ ++static void do_in_ack(void) ++{ ++ gintsts_data_t gintsts; ++ hctsiz_data_t hctsiz; ++ hcchar_data_t hcchar; ++ haint_data_t haint; ++ hcint_data_t hcint; ++ host_grxsts_data_t grxsts; ++ ++ /* Enable HAINTs */ ++ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0001); ++ ++ /* Enable HCINTs */ ++ dwc_write_reg32(&hc_regs->hcintmsk, 0x04a3); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++ /* ++ * Receive Control In packet ++ */ ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ hcchar.b.chdis = 1; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ dwc_mdelay(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 8; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = DWC_OTG_HC_PID_DATA1; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.epdir = 1; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++ /* Wait for receive status queue interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.rxstsqlvl == 0); ++ ++ ++ /* Read RXSTS */ ++ grxsts.d32 = dwc_read_reg32(&global_regs->grxstsp); ++ ++ /* Clear RXSTSQLVL in GINTSTS */ ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ switch (grxsts.b.pktsts) { ++ case DWC_GRXSTS_PKTSTS_IN: ++ /* Read the data into the host buffer */ ++ if (grxsts.b.bcnt > 0) { ++ int i; ++ int word_count = (grxsts.b.bcnt + 3) / 4; ++ ++ data_fifo = (uint32_t *) ((char *)global_regs + 0x1000); ++ ++ for (i = 0; i < word_count; i++) { ++ (void)dwc_read_reg32(data_fifo++); ++ } ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++ /* Wait for receive status queue interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.rxstsqlvl == 0); ++ ++ ++ /* Read RXSTS */ ++ grxsts.d32 = dwc_read_reg32(&global_regs->grxstsp); ++ ++ /* Clear RXSTSQLVL in GINTSTS */ ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ switch (grxsts.b.pktsts) { ++ case DWC_GRXSTS_PKTSTS_IN_XFER_COMP: ++ break; ++ ++ default: ++ break; ++ } ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++// usleep(100000); ++// mdelay(100); ++ dwc_mdelay(1); ++ ++ /* ++ * Send handshake packet ++ */ ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++ /* Make sure channel is disabled */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chen) { ++ hcchar.b.chdis = 1; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ //sleep(1); ++ dwc_mdelay(1000); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ } ++ ++ /* Set HCTSIZ */ ++ hctsiz.d32 = 0; ++ hctsiz.b.xfersize = 0; ++ hctsiz.b.pktcnt = 1; ++ hctsiz.b.pid = DWC_OTG_HC_PID_DATA1; ++ dwc_write_reg32(&hc_regs->hctsiz, hctsiz.d32); ++ ++ /* Set HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.epdir = 0; ++ hcchar.b.epnum = 0; ++ hcchar.b.mps = 8; ++ hcchar.b.chen = 1; ++ dwc_write_reg32(&hc_regs->hcchar, hcchar.d32); ++ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ ++ /* Wait for host channel interrupt */ ++ do { ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++ } while (gintsts.b.hcintr == 0); ++ ++ ++ /* Disable HCINTs */ ++ dwc_write_reg32(&hc_regs->hcintmsk, 0x0000); ++ ++ /* Disable HAINTs */ ++ dwc_write_reg32(&hc_global_regs->haintmsk, 0x0000); ++ ++ /* Read HAINT */ ++ haint.d32 = dwc_read_reg32(&hc_global_regs->haint); ++ ++ /* Read HCINT */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ ++ /* Read HCCHAR */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ ++ /* Clear HCINT */ ++ dwc_write_reg32(&hc_regs->hcint, hcint.d32); ++ ++ /* Clear HAINT */ ++ dwc_write_reg32(&hc_global_regs->haint, haint.d32); ++ ++ /* Clear GINTSTS */ ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ /* Read GINTSTS */ ++ gintsts.d32 = dwc_read_reg32(&global_regs->gintsts); ++} ++#endif ++ ++/** Handles hub class-specific requests. */ ++int dwc_otg_hcd_hub_control(dwc_otg_hcd_t * dwc_otg_hcd, ++ uint16_t typeReq, ++ uint16_t wValue, ++ uint16_t wIndex, uint8_t * buf, uint16_t wLength) ++{ ++ int retval = 0; ++ ++ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; ++ usb_hub_descriptor_t *hub_desc; ++ hprt0_data_t hprt0 = {.d32 = 0 }; ++ ++ uint32_t port_status; ++ ++ switch (typeReq) { ++ case UCR_CLEAR_HUB_FEATURE: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearHubFeature 0x%x\n", wValue); ++ switch (wValue) { ++ case UHF_C_HUB_LOCAL_POWER: ++ case UHF_C_HUB_OVER_CURRENT: ++ /* Nothing required here */ ++ break; ++ default: ++ retval = -DWC_E_INVALID; ++ DWC_ERROR("DWC OTG HCD - " ++ "ClearHubFeature request %xh unknown\n", ++ wValue); ++ } ++ break; ++ case UCR_CLEAR_PORT_FEATURE: ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ if (wValue != UHF_PORT_L1) ++#endif ++ if (!wIndex || wIndex > 1) ++ goto error; ++ ++ switch (wValue) { ++ case UHF_PORT_ENABLE: ++ DWC_DEBUGPL(DBG_ANY, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_ENABLE\n"); ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtena = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case UHF_PORT_SUSPEND: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_SUSPEND\n"); ++ ++ dwc_write_reg32(core_if->pcgcctl, 0); ++ dwc_mdelay(5); ++ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtres = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ hprt0.b.prtsusp = 0; ++ /* Clear Resume bit */ ++ dwc_mdelay(100); ++ hprt0.b.prtres = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ case UHF_PORT_L1: ++ { ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ glpmcfg_data_t lpmcfg = {.d32 = 0 }; ++ ++ lpmcfg.d32 = ++ dwc_read_reg32(&core_if->core_global_regs-> ++ glpmcfg); ++ lpmcfg.b.en_utmi_sleep = 0; ++ lpmcfg.b.hird_thres &= (~(1 << 4)); ++ lpmcfg.b.prt_sleep_sts = 1; ++ dwc_write_reg32(&core_if->core_global_regs-> ++ glpmcfg, lpmcfg.d32); ++ ++ /* Clear Enbl_L1Gating bit. */ ++ pcgcctl.b.enbl_sleep_gating = 1; ++ dwc_modify_reg32(core_if->pcgcctl, pcgcctl.d32, ++ 0); ++ ++ dwc_mdelay(5); ++ ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtres = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, ++ hprt0.d32); ++ /* This bit will be cleared in wakeup interrupt handle */ ++ break; ++ } ++#endif ++ case UHF_PORT_POWER: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_POWER\n"); ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtpwr = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case UHF_PORT_INDICATOR: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_INDICATOR\n"); ++ /* Port inidicator not supported */ ++ break; ++ case UHF_C_PORT_CONNECTION: ++ /* Clears drivers internal connect status change ++ * flag */ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_CONNECTION\n"); ++ dwc_otg_hcd->flags.b.port_connect_status_change = 0; ++ break; ++ case UHF_C_PORT_RESET: ++ /* Clears the driver's internal Port Reset Change ++ * flag */ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_RESET\n"); ++ dwc_otg_hcd->flags.b.port_reset_change = 0; ++ break; ++ case UHF_C_PORT_ENABLE: ++ /* Clears the driver's internal Port ++ * Enable/Disable Change flag */ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_ENABLE\n"); ++ dwc_otg_hcd->flags.b.port_enable_change = 0; ++ break; ++ case UHF_C_PORT_SUSPEND: ++ /* Clears the driver's internal Port Suspend ++ * Change flag, which is set when resume signaling on ++ * the host port is complete */ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_SUSPEND\n"); ++ dwc_otg_hcd->flags.b.port_suspend_change = 0; ++ break; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ case UHF_C_PORT_L1: ++ dwc_otg_hcd->flags.b.port_l1_change = 0; ++ break; ++#endif ++ case UHF_C_PORT_OVER_CURRENT: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "ClearPortFeature USB_PORT_FEAT_C_OVER_CURRENT\n"); ++ dwc_otg_hcd->flags.b.port_over_current_change = 0; ++ break; ++ default: ++ retval = -DWC_E_INVALID; ++ DWC_ERROR("DWC OTG HCD - " ++ "ClearPortFeature request %xh " ++ "unknown or unsupported\n", wValue); ++ } ++ break; ++ case UCR_GET_HUB_DESCRIPTOR: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "GetHubDescriptor\n"); ++ hub_desc = (usb_hub_descriptor_t *) buf; ++ hub_desc->bDescLength = 9; ++ hub_desc->bDescriptorType = 0x29; ++ hub_desc->bNbrPorts = 1; ++ USETW(hub_desc->wHubCharacteristics, 0x08); ++ hub_desc->bPwrOn2PwrGood = 1; ++ hub_desc->bHubContrCurrent = 0; ++ hub_desc->DeviceRemovable[0] = 0; ++ hub_desc->DeviceRemovable[1] = 0xff; ++ break; ++ case UCR_GET_HUB_STATUS: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "GetHubStatus\n"); ++ DWC_MEMSET(buf, 0, 4); ++ break; ++ case UCR_GET_PORT_STATUS: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "GetPortStatus\n"); ++ if (!wIndex || wIndex > 1) ++ goto error; ++ ++ port_status = 0; ++ ++ if (dwc_otg_hcd->flags.b.port_connect_status_change) ++ port_status |= (1 << UHF_C_PORT_CONNECTION); ++ ++ if (dwc_otg_hcd->flags.b.port_enable_change) ++ port_status |= (1 << UHF_C_PORT_ENABLE); ++ ++ if (dwc_otg_hcd->flags.b.port_suspend_change) ++ port_status |= (1 << UHF_C_PORT_SUSPEND); ++ ++ if (dwc_otg_hcd->flags.b.port_l1_change) ++ port_status |= (1 << UHF_C_PORT_L1); ++ ++ if (dwc_otg_hcd->flags.b.port_reset_change) { ++ port_status |= (1 << UHF_C_PORT_RESET); ++ } ++ ++ if (dwc_otg_hcd->flags.b.port_over_current_change) { ++ DWC_ERROR("Device Not Supported\n"); ++ port_status |= (1 << UHF_C_PORT_OVER_CURRENT); ++ } ++ ++ if (!dwc_otg_hcd->flags.b.port_connect_status) { ++ /* ++ * The port is disconnected, which means the core is ++ * either in device mode or it soon will be. Just ++ * return 0's for the remainder of the port status ++ * since the port register can't be read if the core ++ * is in device mode. ++ */ ++ *((__le32 *) buf) = dwc_cpu_to_le32(&port_status); ++ break; ++ } ++ ++ hprt0.d32 = dwc_read_reg32(core_if->host_if->hprt0); ++ DWC_DEBUGPL(DBG_HCDV, " HPRT0: 0x%08x\n", hprt0.d32); ++ ++ if (hprt0.b.prtconnsts) ++ port_status |= (1 << UHF_PORT_CONNECTION); ++ ++ if (hprt0.b.prtena) ++ port_status |= (1 << UHF_PORT_ENABLE); ++ ++ if (hprt0.b.prtsusp) ++ port_status |= (1 << UHF_PORT_SUSPEND); ++ ++ if (hprt0.b.prtovrcurract) ++ port_status |= (1 << UHF_PORT_OVER_CURRENT); ++ ++ if (hprt0.b.prtrst) ++ port_status |= (1 << UHF_PORT_RESET); ++ ++ if (hprt0.b.prtpwr) ++ port_status |= (1 << UHF_PORT_POWER); ++ ++ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) ++ port_status |= (1 << UHF_PORT_HIGH_SPEED); ++ else if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED) ++ port_status |= (1 << UHF_PORT_LOW_SPEED); ++ ++ if (hprt0.b.prttstctl) ++ port_status |= (1 << UHF_PORT_TEST); ++ if (dwc_otg_get_lpm_portsleepstatus(dwc_otg_hcd->core_if)) { ++ port_status |= (1 << UHF_PORT_L1); ++ } ++ ++ /* USB_PORT_FEAT_INDICATOR unsupported always 0 */ ++ ++ *((__le32 *) buf) = dwc_cpu_to_le32(&port_status); ++ ++ break; ++ case UCR_SET_HUB_FEATURE: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetHubFeature\n"); ++ /* No HUB features supported */ ++ break; ++ case UCR_SET_PORT_FEATURE: ++ if (wValue != UHF_PORT_TEST && (!wIndex || wIndex > 1)) ++ goto error; ++ ++ if (!dwc_otg_hcd->flags.b.port_connect_status) { ++ /* ++ * The port is disconnected, which means the core is ++ * either in device mode or it soon will be. Just ++ * return without doing anything since the port ++ * register can't be written if the core is in device ++ * mode. ++ */ ++ break; ++ } ++ ++ switch (wValue) { ++ case UHF_PORT_SUSPEND: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_SUSPEND\n"); ++ if (dwc_otg_hcd_otg_port(dwc_otg_hcd) == wIndex && ++ dwc_otg_hcd->fops->get_b_hnp_enable(dwc_otg_hcd)) { ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ gotgctl.b.hstsethnpen = 1; ++ dwc_modify_reg32(&core_if->core_global_regs-> ++ gotgctl, 0, gotgctl.d32); ++ core_if->op_state = A_SUSPEND; ++ } ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtsusp = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ { ++ uint64_t flags; ++ /* Update lx_state */ ++ DWC_SPINLOCK_IRQSAVE(dwc_otg_hcd->lock, &flags); ++ core_if->lx_state = DWC_OTG_L2; ++ DWC_SPINUNLOCK_IRQRESTORE(dwc_otg_hcd->lock, flags); ++ } ++ /* Suspend the Phy Clock */ ++ { ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ pcgcctl.b.stoppclk = 1; ++ dwc_modify_reg32(core_if->pcgcctl, 0, ++ pcgcctl.d32); ++ } ++ ++ /* For HNP the bus must be suspended for at least 200ms. */ ++ if (dwc_otg_hcd->fops->get_b_hnp_enable(dwc_otg_hcd)) { ++ dwc_mdelay(200); ++ } ++ break; ++ case UHF_PORT_POWER: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_POWER\n"); ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtpwr = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ break; ++ case UHF_PORT_RESET: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_RESET\n"); ++ { ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ pcgcctl.b.enbl_sleep_gating = 1; ++ pcgcctl.b.stoppclk = 1; ++ dwc_modify_reg32(core_if->pcgcctl, pcgcctl.d32, ++ 0); ++ dwc_write_reg32(core_if->pcgcctl, 0); ++ } ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ { ++ glpmcfg_data_t lpmcfg; ++ lpmcfg.d32 = ++ dwc_read_reg32(&core_if->core_global_regs-> ++ glpmcfg); ++ if (lpmcfg.b.prt_sleep_sts) { ++ lpmcfg.b.en_utmi_sleep = 0; ++ lpmcfg.b.hird_thres &= (~(1 << 4)); ++ dwc_write_reg32(&core_if-> ++ core_global_regs-> ++ glpmcfg, lpmcfg.d32); ++ dwc_mdelay(1); ++ } ++ } ++#endif ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ /* When B-Host the Port reset bit is set in ++ * the Start HCD Callback function, so that ++ * the reset is started within 1ms of the HNP ++ * success interrupt. */ ++ if (!dwc_otg_hcd_is_b_host(dwc_otg_hcd)) { ++ hprt0.b.prtrst = 1; ++ dwc_write_reg32(core_if->host_if->hprt0, ++ hprt0.d32); ++ } ++ /* Clear reset bit in 10ms (FS/LS) or 50ms (HS) */ ++ dwc_mdelay(60); ++ hprt0.b.prtrst = 0; ++ dwc_write_reg32(core_if->host_if->hprt0, hprt0.d32); ++ core_if->lx_state = DWC_OTG_L0; /* Now back to the on state */ ++ break; ++#ifdef DWC_HS_ELECT_TST ++ case UHF_PORT_TEST: ++ { ++ uint32_t t; ++ gintmsk_data_t gintmsk; ++ ++ t = (wIndex >> 8); /* MSB wIndex USB */ ++ DWC_DEBUGPL(DBG_HCD, ++ "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_TEST %d\n", ++ t); ++ DWC_WARN("USB_PORT_FEAT_TEST %d\n", t); ++ if (t < 6) { ++ hprt0.d32 = dwc_otg_read_hprt0(core_if); ++ hprt0.b.prttstctl = t; ++ dwc_write_reg32(core_if->host_if->hprt0, ++ hprt0.d32); ++ } else { ++ /* Setup global vars with reg addresses (quick and ++ * dirty hack, should be cleaned up) ++ */ ++ global_regs = core_if->core_global_regs; ++ hc_global_regs = ++ core_if->host_if->host_global_regs; ++ hc_regs = ++ (dwc_otg_hc_regs_t *) ((char *) ++ global_regs + ++ 0x500); ++ data_fifo = ++ (uint32_t *) ((char *)global_regs + ++ 0x1000); ++ ++ if (t == 6) { /* HS_HOST_PORT_SUSPEND_RESUME */ ++ /* Save current interrupt mask */ ++ gintmsk.d32 = ++ dwc_read_reg32 ++ (&global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ dwc_write_reg32(&global_regs-> ++ gintmsk, 0); ++ ++ /* 15 second delay per the test spec */ ++ dwc_mdelay(15000); ++ ++ /* Drive suspend on the root port */ ++ hprt0.d32 = ++ dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtsusp = 1; ++ hprt0.b.prtres = 0; ++ dwc_write_reg32(core_if-> ++ host_if->hprt0, ++ hprt0.d32); ++ ++ /* 15 second delay per the test spec */ ++ dwc_mdelay(15000); ++ ++ /* Drive resume on the root port */ ++ hprt0.d32 = ++ dwc_otg_read_hprt0(core_if); ++ hprt0.b.prtsusp = 0; ++ hprt0.b.prtres = 1; ++ dwc_write_reg32(core_if-> ++ host_if->hprt0, ++ hprt0.d32); ++ dwc_mdelay(100); ++ ++ /* Clear the resume bit */ ++ hprt0.b.prtres = 0; ++ dwc_write_reg32(core_if-> ++ host_if->hprt0, ++ hprt0.d32); ++ ++ /* Restore interrupts */ ++ dwc_write_reg32(&global_regs-> ++ gintmsk, ++ gintmsk.d32); ++ } else if (t == 7) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR setup */ ++ /* Save current interrupt mask */ ++ gintmsk.d32 = ++ dwc_read_reg32 ++ (&global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ dwc_write_reg32(&global_regs-> ++ gintmsk, 0); ++ ++ /* 15 second delay per the test spec */ ++ dwc_mdelay(15000); ++ ++ /* Send the Setup packet */ ++ do_setup(); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ dwc_mdelay(15000); ++ ++ /* Restore interrupts */ ++ dwc_write_reg32(&global_regs-> ++ gintmsk, ++ gintmsk.d32); ++ } else if (t == 8) { /* SINGLE_STEP_GET_DEVICE_DESCRIPTOR execute */ ++ /* Save current interrupt mask */ ++ gintmsk.d32 = ++ dwc_read_reg32 ++ (&global_regs->gintmsk); ++ ++ /* Disable all interrupts while we muck with ++ * the hardware directly ++ */ ++ dwc_write_reg32(&global_regs-> ++ gintmsk, 0); ++ ++ /* Send the Setup packet */ ++ do_setup(); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ dwc_mdelay(15000); ++ ++ /* Send the In and Ack packets */ ++ do_in_ack(); ++ ++ /* 15 second delay so nothing else happens for awhile */ ++ dwc_mdelay(15000); ++ ++ /* Restore interrupts */ ++ dwc_write_reg32(&global_regs-> ++ gintmsk, ++ gintmsk.d32); ++ } ++ } ++ break; ++ } ++#endif /* DWC_HS_ELECT_TST */ ++ ++ case UHF_PORT_INDICATOR: ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB CONTROL - " ++ "SetPortFeature - USB_PORT_FEAT_INDICATOR\n"); ++ /* Not supported */ ++ break; ++ default: ++ retval = -DWC_E_INVALID; ++ DWC_ERROR("DWC OTG HCD - " ++ "SetPortFeature request %xh " ++ "unknown or unsupported\n", wValue); ++ break; ++ } ++ break; ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ case UCR_SET_AND_TEST_PORT_FEATURE: ++ if (wValue != UHF_PORT_L1) { ++ goto error; ++ } ++ { ++ int portnum, hird, devaddr, remwake; ++ glpmcfg_data_t lpmcfg; ++ uint32_t time_usecs; ++ gintsts_data_t gintsts; ++ gintmsk_data_t gintmsk; ++ ++ if (!dwc_otg_get_param_lpm_enable(core_if)) { ++ goto error; ++ } ++ if (wValue != UHF_PORT_L1 || wLength != 1) { ++ goto error; ++ } ++ /* Check if the port currently is in SLEEP state */ ++ lpmcfg.d32 = ++ dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ if (lpmcfg.b.prt_sleep_sts) { ++ DWC_INFO("Port is already in sleep mode\n"); ++ buf[0] = 0; /* Return success */ ++ break; ++ } ++ ++ portnum = wIndex & 0xf; ++ hird = (wIndex >> 4) & 0xf; ++ devaddr = (wIndex >> 8) & 0x7f; ++ remwake = (wIndex >> 15); ++ ++ if (portnum != 1) { ++ retval = -DWC_E_INVALID; ++ DWC_WARN ++ ("Wrong port number(%d) in SetandTestPortFeature request\n", ++ portnum); ++ break; ++ } ++ ++ DWC_PRINTF ++ ("SetandTestPortFeature request: portnum = %d, hird = %d, devaddr = %d, rewake = %d\n", ++ portnum, hird, devaddr, remwake); ++ /* Disable LPM interrupt */ ++ gintmsk.d32 = 0; ++ gintmsk.b.lpmtranrcvd = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, ++ gintmsk.d32, 0); ++ ++ if (dwc_otg_hcd_send_lpm ++ (dwc_otg_hcd, devaddr, hird, remwake)) { ++ retval = -DWC_E_INVALID; ++ break; ++ } ++ ++ time_usecs = 10 * (lpmcfg.b.retry_count + 1); ++ /* We will consider timeout if time_usecs microseconds pass, ++ * and we don't receive LPM transaction status. ++ * After receiving non-error responce(ACK/NYET/STALL) from device, ++ * core will set lpmtranrcvd bit. ++ */ ++ do { ++ gintsts.d32 = ++ dwc_read_reg32(&core_if->core_global_regs-> ++ gintsts); ++ if (gintsts.b.lpmtranrcvd) { ++ break; ++ } ++ dwc_udelay(1); ++ } while (--time_usecs); ++ /* lpm_int bit will be cleared in LPM interrupt handler */ ++ ++ /* Now fill status ++ * 0x00 - Success ++ * 0x10 - NYET ++ * 0x11 - Timeout ++ */ ++ if (!gintsts.b.lpmtranrcvd) { ++ buf[0] = 0x3; /* Completion code is Timeout */ ++ dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd); ++ } else { ++ lpmcfg.d32 = ++ dwc_read_reg32(&core_if->core_global_regs-> ++ glpmcfg); ++ if (lpmcfg.b.lpm_resp == 0x3) { ++ /* ACK responce from the device */ ++ buf[0] = 0x00; /* Success */ ++ } else if (lpmcfg.b.lpm_resp == 0x2) { ++ /* NYET responce from the device */ ++ buf[0] = 0x2; ++ } else { ++ /* Otherwise responce with Timeout */ ++ buf[0] = 0x3; ++ } ++ } ++ DWC_PRINTF("Device responce to LPM trans is %x\n", ++ lpmcfg.b.lpm_resp); ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, 0, ++ gintmsk.d32); ++ ++ break; ++ } ++#endif /* CONFIG_USB_DWC_OTG_LPM */ ++ default: ++ error: ++ retval = -DWC_E_INVALID; ++ DWC_WARN("DWC OTG HCD - " ++ "Unknown hub control request type or invalid typeReq: %xh wIndex: %xh wValue: %xh\n", ++ typeReq, wIndex, wValue); ++ break; ++ } ++ ++ return retval; ++} ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++/** Returns index of host channel to perform LPM transaction. */ ++int dwc_otg_hcd_get_hc_for_lpm_tran(dwc_otg_hcd_t * hcd, uint8_t devaddr) ++{ ++ dwc_otg_core_if_t *core_if = hcd->core_if; ++ dwc_hc_t *hc; ++ hcchar_data_t hcchar; ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ ++ if (DWC_CIRCLEQ_EMPTY(&hcd->free_hc_list)) { ++ DWC_PRINTF("No free channel to select for LPM transaction\n"); ++ return -1; ++ } ++ ++ hc = DWC_CIRCLEQ_FIRST(&hcd->free_hc_list); ++ ++ /* Mask host channel interrupts. */ ++ gintmsk.b.hcintr = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, gintmsk.d32, 0); ++ ++ /* Fill fields that core needs for LPM transaction */ ++ hcchar.b.devaddr = devaddr; ++ hcchar.b.epnum = 0; ++ hcchar.b.eptype = DWC_OTG_EP_TYPE_CONTROL; ++ hcchar.b.mps = 64; ++ hcchar.b.lspddev = (hc->speed == DWC_OTG_EP_SPEED_LOW); ++ hcchar.b.epdir = 0; /* OUT */ ++ dwc_write_reg32(&core_if->host_if->hc_regs[hc->hc_num]->hcchar, ++ hcchar.d32); ++ ++ /* Remove the host channel from the free list. */ ++ DWC_CIRCLEQ_REMOVE_INIT(&hcd->free_hc_list, hc, hc_list_entry); ++ ++ DWC_PRINTF("hcnum = %d devaddr = %d\n", hc->hc_num, devaddr); ++ ++ return hc->hc_num; ++} ++ ++/** Release hc after performing LPM transaction */ ++void dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd_t * hcd) ++{ ++ dwc_hc_t *hc; ++ glpmcfg_data_t lpmcfg; ++ uint8_t hc_num; ++ ++ lpmcfg.d32 = dwc_read_reg32(&hcd->core_if->core_global_regs->glpmcfg); ++ hc_num = lpmcfg.b.lpm_chan_index; ++ ++ hc = hcd->hc_ptr_array[hc_num]; ++ ++ DWC_PRINTF("Freeing channel %d after LPM\n", hc_num); ++ /* Return host channel to free list */ ++ DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry); ++} ++ ++int dwc_otg_hcd_send_lpm(dwc_otg_hcd_t * hcd, uint8_t devaddr, uint8_t hird, ++ uint8_t bRemoteWake) ++{ ++ glpmcfg_data_t lpmcfg; ++ pcgcctl_data_t pcgcctl = {.d32 = 0 }; ++ int channel; ++ ++ channel = dwc_otg_hcd_get_hc_for_lpm_tran(hcd, devaddr); ++ if (channel < 0) { ++ return channel; ++ } ++ ++ pcgcctl.b.enbl_sleep_gating = 1; ++ dwc_modify_reg32(hcd->core_if->pcgcctl, 0, pcgcctl.d32); ++ ++ /* Read LPM config register */ ++ lpmcfg.d32 = dwc_read_reg32(&hcd->core_if->core_global_regs->glpmcfg); ++ ++ /* Program LPM transaction fields */ ++ lpmcfg.b.rem_wkup_en = bRemoteWake; ++ lpmcfg.b.hird = hird; ++ lpmcfg.b.hird_thres = 0x1c; ++ lpmcfg.b.lpm_chan_index = channel; ++ lpmcfg.b.en_utmi_sleep = 1; ++ /* Program LPM config register */ ++ dwc_write_reg32(&hcd->core_if->core_global_regs->glpmcfg, lpmcfg.d32); ++ ++ /* Send LPM transaction */ ++ lpmcfg.b.send_lpm = 1; ++ dwc_write_reg32(&hcd->core_if->core_global_regs->glpmcfg, lpmcfg.d32); ++ ++ return 0; ++} ++ ++#endif /* CONFIG_USB_DWC_OTG_LPM */ ++ ++int dwc_otg_hcd_is_status_changed(dwc_otg_hcd_t * hcd, int port) ++{ ++ int retval; ++ ++ if (port != 1) { ++ return -DWC_E_INVALID; ++ } ++ ++ retval = (hcd->flags.b.port_connect_status_change || ++ hcd->flags.b.port_reset_change || ++ hcd->flags.b.port_enable_change || ++ hcd->flags.b.port_suspend_change || ++ hcd->flags.b.port_over_current_change); ++#ifdef DEBUG ++ if (retval) { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD HUB STATUS DATA:" ++ " Root port status changed\n"); ++ DWC_DEBUGPL(DBG_HCDV, " port_connect_status_change: %d\n", ++ hcd->flags.b.port_connect_status_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_reset_change: %d\n", ++ hcd->flags.b.port_reset_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_enable_change: %d\n", ++ hcd->flags.b.port_enable_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_suspend_change: %d\n", ++ hcd->flags.b.port_suspend_change); ++ DWC_DEBUGPL(DBG_HCDV, " port_over_current_change: %d\n", ++ hcd->flags.b.port_over_current_change); ++ } ++#endif ++ return retval; ++} ++ ++int dwc_otg_hcd_get_frame_number(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ hfnum_data_t hfnum; ++ hfnum.d32 = dwc_read_reg32(&dwc_otg_hcd->core_if-> ++ host_if->host_global_regs->hfnum); ++ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD GET FRAME NUMBER %d\n", ++ hfnum.b.frnum); ++#endif ++ return hfnum.b.frnum; ++} ++ ++int dwc_otg_hcd_start(dwc_otg_hcd_t * hcd, ++ struct dwc_otg_hcd_function_ops *fops) ++{ ++ int retval = 0; ++ ++ hcd->fops = fops; ++ if (!dwc_otg_is_device_mode(hcd->core_if)) { ++ dwc_otg_hcd_reinit(hcd); ++ } else { ++ retval = -DWC_E_NO_DEVICE; ++ } ++ ++ return retval; ++} ++ ++void *dwc_otg_hcd_get_priv_data(dwc_otg_hcd_t * hcd) ++{ ++ return hcd->priv; ++} ++ ++void dwc_otg_hcd_set_priv_data(dwc_otg_hcd_t * hcd, void *priv_data) ++{ ++ hcd->priv = priv_data; ++} ++ ++uint32_t dwc_otg_hcd_otg_port(dwc_otg_hcd_t * hcd) ++{ ++ return hcd->otg_port; ++} ++ ++uint32_t dwc_otg_hcd_is_b_host(dwc_otg_hcd_t * hcd) ++{ ++ uint32_t is_b_host; ++ if (hcd->core_if->op_state == B_HOST) { ++ is_b_host = 1; ++ } else { ++ is_b_host = 0; ++ } ++ ++ return is_b_host; ++} ++ ++dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc(dwc_otg_hcd_t * hcd, ++ int iso_desc_count, int atomic_alloc) ++{ ++ dwc_otg_hcd_urb_t *dwc_otg_urb; ++ uint32_t size; ++ ++ size = ++ sizeof(*dwc_otg_urb) + ++ iso_desc_count * sizeof(struct dwc_otg_hcd_iso_packet_desc); ++ if (atomic_alloc) { ++ dwc_otg_urb = dwc_alloc_atomic(size); ++ } else { ++ dwc_otg_urb = dwc_alloc(size); ++ } ++ dwc_otg_urb->packet_count = iso_desc_count; ++ ++ return dwc_otg_urb; ++} ++ ++void dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_hcd_urb_t * dwc_otg_urb, ++ uint8_t dev_addr, uint8_t ep_num, ++ uint8_t ep_type, uint8_t ep_dir, uint16_t mps) ++{ ++ dwc_otg_hcd_fill_pipe(&dwc_otg_urb->pipe_info, dev_addr, ep_num, ++ ep_type, ep_dir, mps); ++#if 0 ++ DWC_PRINTF ++ ("addr = %d, ep_num = %d, ep_dir = 0x%x, ep_type = 0x%x, mps = %d\n", ++ dev_addr, ep_num, ep_dir, ep_type, mps); ++#endif ++} ++ ++void dwc_otg_hcd_urb_set_params(dwc_otg_hcd_urb_t * dwc_otg_urb, ++ void *urb_handle, void *buf, dwc_dma_t dma, ++ uint32_t buflen, void *setup_packet, ++ dwc_dma_t setup_dma, uint32_t flags, ++ uint16_t interval) ++{ ++ dwc_otg_urb->priv = urb_handle; ++ dwc_otg_urb->buf = buf; ++ dwc_otg_urb->dma = dma; ++ dwc_otg_urb->length = buflen; ++ dwc_otg_urb->setup_packet = setup_packet; ++ dwc_otg_urb->setup_dma = setup_dma; ++ dwc_otg_urb->flags = flags; ++ dwc_otg_urb->interval = interval; ++ dwc_otg_urb->status = -DWC_E_IN_PROGRESS; ++} ++ ++uint32_t dwc_otg_hcd_urb_get_status(dwc_otg_hcd_urb_t * dwc_otg_urb) ++{ ++ return dwc_otg_urb->status; ++} ++ ++uint32_t dwc_otg_hcd_urb_get_actual_length(dwc_otg_hcd_urb_t * dwc_otg_urb) ++{ ++ return dwc_otg_urb->actual_length; ++} ++ ++uint32_t dwc_otg_hcd_urb_get_error_count(dwc_otg_hcd_urb_t * dwc_otg_urb) ++{ ++ return dwc_otg_urb->error_count; ++} ++ ++void dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_hcd_urb_t * dwc_otg_urb, ++ int desc_num, uint32_t offset, ++ uint32_t length) ++{ ++ dwc_otg_urb->iso_descs[desc_num].offset = offset; ++ dwc_otg_urb->iso_descs[desc_num].length = length; ++} ++ ++uint32_t dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_hcd_urb_t * dwc_otg_urb, ++ int desc_num) ++{ ++ return dwc_otg_urb->iso_descs[desc_num].status; ++} ++ ++uint32_t dwc_otg_hcd_urb_get_iso_desc_actual_length(dwc_otg_hcd_urb_t * ++ dwc_otg_urb, int desc_num) ++{ ++ return dwc_otg_urb->iso_descs[desc_num].actual_length; ++} ++ ++int dwc_otg_hcd_is_bandwidth_allocated(dwc_otg_hcd_t * hcd, void *ep_handle) ++{ ++ int allocated = 0; ++ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle; ++ ++ if (qh) { ++ if (!DWC_LIST_EMPTY(&qh->qh_list_entry)) { ++ allocated = 1; ++ } ++ } ++ return allocated; ++} ++ ++int dwc_otg_hcd_is_bandwidth_freed(dwc_otg_hcd_t * hcd, void *ep_handle) ++{ ++ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle; ++ int freed = 0; ++ DWC_ASSERT(qh, "qh is not allocated\n"); ++ ++ if (DWC_LIST_EMPTY(&qh->qh_list_entry)) { ++ freed = 1; ++ } ++ ++ return freed; ++} ++ ++uint8_t dwc_otg_hcd_get_ep_bandwidth(dwc_otg_hcd_t * hcd, void *ep_handle) ++{ ++ dwc_otg_qh_t *qh = (dwc_otg_qh_t *) ep_handle; ++ DWC_ASSERT(qh, "qh is not allocated\n"); ++ return qh->usecs; ++} ++ ++void dwc_otg_hcd_dump_state(dwc_otg_hcd_t * hcd) ++{ ++#ifdef DEBUG ++ int num_channels; ++ int i; ++ gnptxsts_data_t np_tx_status; ++ hptxsts_data_t p_tx_status; ++ ++ num_channels = hcd->core_if->core_params->host_channels; ++ DWC_PRINTF("\n"); ++ DWC_PRINTF ++ ("************************************************************\n"); ++ DWC_PRINTF("HCD State:\n"); ++ DWC_PRINTF(" Num channels: %d\n", num_channels); ++ for (i = 0; i < num_channels; i++) { ++ dwc_hc_t *hc = hcd->hc_ptr_array[i]; ++ DWC_PRINTF(" Channel %d:\n", i); ++ DWC_PRINTF(" dev_addr: %d, ep_num: %d, ep_is_in: %d\n", ++ hc->dev_addr, hc->ep_num, hc->ep_is_in); ++ DWC_PRINTF(" speed: %d\n", hc->speed); ++ DWC_PRINTF(" ep_type: %d\n", hc->ep_type); ++ DWC_PRINTF(" max_packet: %d\n", hc->max_packet); ++ DWC_PRINTF(" data_pid_start: %d\n", hc->data_pid_start); ++ DWC_PRINTF(" multi_count: %d\n", hc->multi_count); ++ DWC_PRINTF(" xfer_started: %d\n", hc->xfer_started); ++ DWC_PRINTF(" xfer_buff: %p\n", hc->xfer_buff); ++ DWC_PRINTF(" xfer_len: %d\n", hc->xfer_len); ++ DWC_PRINTF(" xfer_count: %d\n", hc->xfer_count); ++ DWC_PRINTF(" halt_on_queue: %d\n", hc->halt_on_queue); ++ DWC_PRINTF(" halt_pending: %d\n", hc->halt_pending); ++ DWC_PRINTF(" halt_status: %d\n", hc->halt_status); ++ DWC_PRINTF(" do_split: %d\n", hc->do_split); ++ DWC_PRINTF(" complete_split: %d\n", hc->complete_split); ++ DWC_PRINTF(" hub_addr: %d\n", hc->hub_addr); ++ DWC_PRINTF(" port_addr: %d\n", hc->port_addr); ++ DWC_PRINTF(" xact_pos: %d\n", hc->xact_pos); ++ DWC_PRINTF(" requests: %d\n", hc->requests); ++ DWC_PRINTF(" qh: %p\n", hc->qh); ++ if (hc->xfer_started) { ++ hfnum_data_t hfnum; ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ hfnum.d32 = ++ dwc_read_reg32(&hcd->core_if->host_if-> ++ host_global_regs->hfnum); ++ hcchar.d32 = ++ dwc_read_reg32(&hcd->core_if->host_if->hc_regs[i]-> ++ hcchar); ++ hctsiz.d32 = ++ dwc_read_reg32(&hcd->core_if->host_if->hc_regs[i]-> ++ hctsiz); ++ hcint.d32 = ++ dwc_read_reg32(&hcd->core_if->host_if->hc_regs[i]-> ++ hcint); ++ hcintmsk.d32 = ++ dwc_read_reg32(&hcd->core_if->host_if->hc_regs[i]-> ++ hcintmsk); ++ DWC_PRINTF(" hfnum: 0x%08x\n", hfnum.d32); ++ DWC_PRINTF(" hcchar: 0x%08x\n", hcchar.d32); ++ DWC_PRINTF(" hctsiz: 0x%08x\n", hctsiz.d32); ++ DWC_PRINTF(" hcint: 0x%08x\n", hcint.d32); ++ DWC_PRINTF(" hcintmsk: 0x%08x\n", hcintmsk.d32); ++ } ++ if (hc->xfer_started && hc->qh) { ++ dwc_otg_qtd_t *qtd; ++ dwc_otg_hcd_urb_t *urb; ++ ++ DWC_CIRCLEQ_FOREACH(qtd, &hc->qh->qtd_list, qtd_list_entry) { ++ if(!qtd->in_process) ++ break; ++ ++ urb = qtd->urb; ++ DWC_PRINTF(" URB Info:\n"); ++ DWC_PRINTF(" qtd: %p, urb: %p\n", qtd, urb); ++ if (urb) { ++ DWC_PRINTF(" Dev: %d, EP: %d %s\n", ++ dwc_otg_hcd_get_dev_addr(&urb-> ++ pipe_info), ++ dwc_otg_hcd_get_ep_num(&urb-> ++ pipe_info), ++ dwc_otg_hcd_is_pipe_in(&urb-> ++ pipe_info) ? ++ "IN" : "OUT"); ++ DWC_PRINTF(" Max packet size: %d\n", ++ dwc_otg_hcd_get_mps(&urb-> ++ pipe_info)); ++ DWC_PRINTF(" transfer_buffer: %p\n", ++ urb->buf); ++ DWC_PRINTF(" transfer_dma: %p\n", ++ (void *)urb->dma); ++ DWC_PRINTF(" transfer_buffer_length: %d\n", ++ urb->length); ++ DWC_PRINTF(" actual_length: %d\n", ++ urb->actual_length); ++ } ++ } ++ } ++ } ++ DWC_PRINTF(" non_periodic_channels: %d\n", hcd->non_periodic_channels); ++ DWC_PRINTF(" periodic_channels: %d\n", hcd->periodic_channels); ++ DWC_PRINTF(" periodic_usecs: %d\n", hcd->periodic_usecs); ++ np_tx_status.d32 = ++ dwc_read_reg32(&hcd->core_if->core_global_regs->gnptxsts); ++ DWC_PRINTF(" NP Tx Req Queue Space Avail: %d\n", ++ np_tx_status.b.nptxqspcavail); ++ DWC_PRINTF(" NP Tx FIFO Space Avail: %d\n", ++ np_tx_status.b.nptxfspcavail); ++ p_tx_status.d32 = ++ dwc_read_reg32(&hcd->core_if->host_if->host_global_regs->hptxsts); ++ DWC_PRINTF(" P Tx Req Queue Space Avail: %d\n", ++ p_tx_status.b.ptxqspcavail); ++ DWC_PRINTF(" P Tx FIFO Space Avail: %d\n", p_tx_status.b.ptxfspcavail); ++ dwc_otg_hcd_dump_frrem(hcd); ++ dwc_otg_dump_global_registers(hcd->core_if); ++ dwc_otg_dump_host_registers(hcd->core_if); ++ DWC_PRINTF ++ ("************************************************************\n"); ++ DWC_PRINTF("\n"); ++#endif ++} ++ ++#ifdef DEBUG ++void dwc_print_setup_data(uint8_t * setup) ++{ ++ int i; ++ if (CHK_DEBUG_LEVEL(DBG_HCD)) { ++ DWC_PRINTF("Setup Data = MSB "); ++ for (i = 7; i >= 0; i--) ++ DWC_PRINTF("%02x ", setup[i]); ++ DWC_PRINTF("\n"); ++ DWC_PRINTF(" bmRequestType Tranfer = %s\n", ++ (setup[0] & 0x80) ? "Device-to-Host" : ++ "Host-to-Device"); ++ DWC_PRINTF(" bmRequestType Type = "); ++ switch ((setup[0] & 0x60) >> 5) { ++ case 0: ++ DWC_PRINTF("Standard\n"); ++ break; ++ case 1: ++ DWC_PRINTF("Class\n"); ++ break; ++ case 2: ++ DWC_PRINTF("Vendor\n"); ++ break; ++ case 3: ++ DWC_PRINTF("Reserved\n"); ++ break; ++ } ++ DWC_PRINTF(" bmRequestType Recipient = "); ++ switch (setup[0] & 0x1f) { ++ case 0: ++ DWC_PRINTF("Device\n"); ++ break; ++ case 1: ++ DWC_PRINTF("Interface\n"); ++ break; ++ case 2: ++ DWC_PRINTF("Endpoint\n"); ++ break; ++ case 3: ++ DWC_PRINTF("Other\n"); ++ break; ++ default: ++ DWC_PRINTF("Reserved\n"); ++ break; ++ } ++ DWC_PRINTF(" bRequest = 0x%0x\n", setup[1]); ++ DWC_PRINTF(" wValue = 0x%0x\n", *((uint16_t *) & setup[2])); ++ DWC_PRINTF(" wIndex = 0x%0x\n", *((uint16_t *) & setup[4])); ++ DWC_PRINTF(" wLength = 0x%0x\n\n", *((uint16_t *) & setup[6])); ++ } ++} ++#endif ++ ++void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t * hcd) ++{ ++#if 0 ++ DWC_PRINTF("Frame remaining at SOF:\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->frrem_samples, hcd->frrem_accum, ++ (hcd->frrem_samples > 0) ? ++ hcd->frrem_accum / hcd->frrem_samples : 0); ++ ++ DWC_PRINTF("\n"); ++ DWC_PRINTF("Frame remaining at start_transfer (uframe 7):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->core_if->hfnum_7_samples, ++ hcd->core_if->hfnum_7_frrem_accum, ++ (hcd->core_if->hfnum_7_samples > ++ 0) ? hcd->core_if->hfnum_7_frrem_accum / ++ hcd->core_if->hfnum_7_samples : 0); ++ DWC_PRINTF("Frame remaining at start_transfer (uframe 0):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->core_if->hfnum_0_samples, ++ hcd->core_if->hfnum_0_frrem_accum, ++ (hcd->core_if->hfnum_0_samples > ++ 0) ? hcd->core_if->hfnum_0_frrem_accum / ++ hcd->core_if->hfnum_0_samples : 0); ++ DWC_PRINTF("Frame remaining at start_transfer (uframe 1-6):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->core_if->hfnum_other_samples, ++ hcd->core_if->hfnum_other_frrem_accum, ++ (hcd->core_if->hfnum_other_samples > ++ 0) ? hcd->core_if->hfnum_other_frrem_accum / ++ hcd->core_if->hfnum_other_samples : 0); ++ ++ DWC_PRINTF("\n"); ++ DWC_PRINTF("Frame remaining at sample point A (uframe 7):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_7_samples_a, hcd->hfnum_7_frrem_accum_a, ++ (hcd->hfnum_7_samples_a > 0) ? ++ hcd->hfnum_7_frrem_accum_a / hcd->hfnum_7_samples_a : 0); ++ DWC_PRINTF("Frame remaining at sample point A (uframe 0):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_0_samples_a, hcd->hfnum_0_frrem_accum_a, ++ (hcd->hfnum_0_samples_a > 0) ? ++ hcd->hfnum_0_frrem_accum_a / hcd->hfnum_0_samples_a : 0); ++ DWC_PRINTF("Frame remaining at sample point A (uframe 1-6):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_other_samples_a, hcd->hfnum_other_frrem_accum_a, ++ (hcd->hfnum_other_samples_a > 0) ? ++ hcd->hfnum_other_frrem_accum_a / ++ hcd->hfnum_other_samples_a : 0); ++ ++ DWC_PRINTF("\n"); ++ DWC_PRINTF("Frame remaining at sample point B (uframe 7):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_7_samples_b, hcd->hfnum_7_frrem_accum_b, ++ (hcd->hfnum_7_samples_b > 0) ? ++ hcd->hfnum_7_frrem_accum_b / hcd->hfnum_7_samples_b : 0); ++ DWC_PRINTF("Frame remaining at sample point B (uframe 0):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_0_samples_b, hcd->hfnum_0_frrem_accum_b, ++ (hcd->hfnum_0_samples_b > 0) ? ++ hcd->hfnum_0_frrem_accum_b / hcd->hfnum_0_samples_b : 0); ++ DWC_PRINTF("Frame remaining at sample point B (uframe 1-6):\n"); ++ DWC_PRINTF(" samples %u, accum %llu, avg %llu\n", ++ hcd->hfnum_other_samples_b, hcd->hfnum_other_frrem_accum_b, ++ (hcd->hfnum_other_samples_b > 0) ? ++ hcd->hfnum_other_frrem_accum_b / ++ hcd->hfnum_other_samples_b : 0); ++#endif ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h +@@ -0,0 +1,804 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd.h $ ++ * $Revision: #52 $ ++ * $Date: 2009/04/21 $ ++ * $Change: 1237472 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++#ifndef __DWC_HCD_H__ ++#define __DWC_HCD_H__ ++ ++#include <usb.h> ++#include "dwc_otg_hcd_if.h" ++#include "dwc_otg_core_if.h" ++#include "dwc_list.h" ++#include "dwc_otg_cil.h" ++ ++/** ++ * @file ++ * ++ * This file contains the structures, constants, and interfaces for ++ * the Host Contoller Driver (HCD). ++ * ++ * The Host Controller Driver (HCD) is responsible for translating requests ++ * from the USB Driver into the appropriate actions on the DWC_otg controller. ++ * It isolates the USBD from the specifics of the controller by providing an ++ * API to the USBD. ++ */ ++ ++struct dwc_otg_hcd_pipe_info { ++ uint8_t dev_addr; ++ uint8_t ep_num; ++ uint8_t pipe_type; ++ uint8_t pipe_dir; ++ uint16_t mps; ++}; ++ ++struct dwc_otg_hcd_iso_packet_desc { ++ uint32_t offset; ++ uint32_t length; ++ uint32_t actual_length; ++ uint32_t status; ++}; ++ ++struct dwc_otg_qtd; ++ ++struct dwc_otg_hcd_urb { ++ void *priv; ++ struct dwc_otg_qtd *qtd; ++ void *buf; ++ dwc_dma_t dma; ++ void *setup_packet; ++ dwc_dma_t setup_dma; ++ uint32_t length; ++ uint32_t actual_length; ++ uint32_t status; ++ uint32_t error_count; ++ uint32_t packet_count; ++ uint32_t flags; ++ uint16_t interval; ++ struct dwc_otg_hcd_pipe_info pipe_info; ++ struct dwc_otg_hcd_iso_packet_desc iso_descs[0]; ++}; ++ ++static inline uint8_t dwc_otg_hcd_get_ep_num(struct dwc_otg_hcd_pipe_info *pipe) ++{ ++ return pipe->ep_num; ++} ++ ++static inline uint8_t dwc_otg_hcd_get_pipe_type(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return pipe->pipe_type; ++} ++ ++static inline uint16_t dwc_otg_hcd_get_mps(struct dwc_otg_hcd_pipe_info *pipe) ++{ ++ return pipe->mps; ++} ++ ++static inline uint8_t dwc_otg_hcd_get_dev_addr(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return pipe->dev_addr; ++} ++ ++static inline uint8_t dwc_otg_hcd_is_pipe_isoc(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return (pipe->pipe_type == UE_ISOCHRONOUS); ++} ++ ++static inline uint8_t dwc_otg_hcd_is_pipe_int(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return (pipe->pipe_type == UE_INTERRUPT); ++} ++ ++static inline uint8_t dwc_otg_hcd_is_pipe_bulk(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return (pipe->pipe_type == UE_BULK); ++} ++ ++static inline uint8_t dwc_otg_hcd_is_pipe_control(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return (pipe->pipe_type == UE_CONTROL); ++} ++ ++static inline uint8_t dwc_otg_hcd_is_pipe_in(struct dwc_otg_hcd_pipe_info *pipe) ++{ ++ return (pipe->pipe_dir == UE_DIR_IN); ++} ++ ++static inline uint8_t dwc_otg_hcd_is_pipe_out(struct dwc_otg_hcd_pipe_info ++ *pipe) ++{ ++ return (!dwc_otg_hcd_is_pipe_in(pipe)); ++} ++ ++static inline void dwc_otg_hcd_fill_pipe(struct dwc_otg_hcd_pipe_info *pipe, ++ uint8_t devaddr, uint8_t ep_num, ++ uint8_t pipe_type, uint8_t pipe_dir, ++ uint16_t mps) ++{ ++ pipe->dev_addr = devaddr; ++ pipe->ep_num = ep_num; ++ pipe->pipe_type = pipe_type; ++ pipe->pipe_dir = pipe_dir; ++ pipe->mps = mps; ++} ++ ++/** ++ * Phases for control transfers. ++ */ ++typedef enum dwc_otg_control_phase { ++ DWC_OTG_CONTROL_SETUP, ++ DWC_OTG_CONTROL_DATA, ++ DWC_OTG_CONTROL_STATUS ++} dwc_otg_control_phase_e; ++ ++/** Transaction types. */ ++typedef enum dwc_otg_transaction_type { ++ DWC_OTG_TRANSACTION_NONE, ++ DWC_OTG_TRANSACTION_PERIODIC, ++ DWC_OTG_TRANSACTION_NON_PERIODIC, ++ DWC_OTG_TRANSACTION_ALL ++} dwc_otg_transaction_type_e; ++ ++struct dwc_otg_qh; ++ ++/** ++ * A Queue Transfer Descriptor (QTD) holds the state of a bulk, control, ++ * interrupt, or isochronous transfer. A single QTD is created for each URB ++ * (of one of these types) submitted to the HCD. The transfer associated with ++ * a QTD may require one or multiple transactions. ++ * ++ * A QTD is linked to a Queue Head, which is entered in either the ++ * non-periodic or periodic schedule for execution. When a QTD is chosen for ++ * execution, some or all of its transactions may be executed. After ++ * execution, the state of the QTD is updated. The QTD may be retired if all ++ * its transactions are complete or if an error occurred. Otherwise, it ++ * remains in the schedule so more transactions can be executed later. ++ */ ++typedef struct dwc_otg_qtd { ++ /** ++ * Determines the PID of the next data packet for the data phase of ++ * control transfers. Ignored for other transfer types.<br> ++ * One of the following values: ++ * - DWC_OTG_HC_PID_DATA0 ++ * - DWC_OTG_HC_PID_DATA1 ++ */ ++ uint8_t data_toggle; ++ ++ /** Current phase for control transfers (Setup, Data, or Status). */ ++ dwc_otg_control_phase_e control_phase; ++ ++ /** Keep track of the current split type ++ * for FS/LS endpoints on a HS Hub */ ++ uint8_t complete_split; ++ ++ /** How many bytes transferred during SSPLIT OUT */ ++ uint32_t ssplit_out_xfer_count; ++ ++ /** ++ * Holds the number of bus errors that have occurred for a transaction ++ * within this transfer. ++ */ ++ uint8_t error_count; ++ ++ /** ++ * Index of the next frame descriptor for an isochronous transfer. A ++ * frame descriptor describes the buffer position and length of the ++ * data to be transferred in the next scheduled (micro)frame of an ++ * isochronous transfer. It also holds status for that transaction. ++ * The frame index starts at 0. ++ */ ++ uint16_t isoc_frame_index; ++ ++ /** Position of the ISOC split on full/low speed */ ++ uint8_t isoc_split_pos; ++ ++ /** Position of the ISOC split in the buffer for the current frame */ ++ uint16_t isoc_split_offset; ++ ++ /** URB for this transfer */ ++ struct dwc_otg_hcd_urb *urb; ++ ++ struct dwc_otg_qh *qh; ++ ++ /** This list of QTDs */ ++ DWC_CIRCLEQ_ENTRY(dwc_otg_qtd) qtd_list_entry; ++ ++ /** Indicates if this QTD is currently processed by HW. */ ++ uint8_t in_process; ++ ++ /** Number of DMA descriptors for this QTD */ ++ uint8_t n_desc; ++ ++ /** ++ * Last activated frame(packet) index. ++ * Used in Descriptor DMA mode only. ++ */ ++ uint16_t isoc_frame_index_last; ++ ++} dwc_otg_qtd_t; ++ ++DWC_CIRCLEQ_HEAD(dwc_otg_qtd_list, dwc_otg_qtd); ++ ++/** ++ * A Queue Head (QH) holds the static characteristics of an endpoint and ++ * maintains a list of transfers (QTDs) for that endpoint. A QH structure may ++ * be entered in either the non-periodic or periodic schedule. ++ */ ++typedef struct dwc_otg_qh { ++ /** ++ * Endpoint type. ++ * One of the following values: ++ * - UE_CONTROL ++ * - UE_BULK ++ * - UE_INTERRUPT ++ * - UE_ISOCHRONOUS ++ */ ++ uint8_t ep_type; ++ uint8_t ep_is_in; ++ ++ /** wMaxPacketSize Field of Endpoint Descriptor. */ ++ uint16_t maxp; ++ ++ /** ++ * Device speed. ++ * One of the following values: ++ * - DWC_OTG_EP_SPEED_LOW ++ * - DWC_OTG_EP_SPEED_FULL ++ * - DWC_OTG_EP_SPEED_HIGH ++ */ ++ uint8_t dev_speed; ++ ++ /** ++ * Determines the PID of the next data packet for non-control ++ * transfers. Ignored for control transfers.<br> ++ * One of the following values: ++ * - DWC_OTG_HC_PID_DATA0 ++ * - DWC_OTG_HC_PID_DATA1 ++ */ ++ uint8_t data_toggle; ++ ++ /** Ping state if 1. */ ++ uint8_t ping_state; ++ ++ /** ++ * List of QTDs for this QH. ++ */ ++ struct dwc_otg_qtd_list qtd_list; ++ ++ /** Host channel currently processing transfers for this QH. */ ++ struct dwc_hc *channel; ++ ++ /** Full/low speed endpoint on high-speed hub requires split. */ ++ uint8_t do_split; ++ ++ /** @name Periodic schedule information */ ++ /** @{ */ ++ ++ /** Bandwidth in microseconds per (micro)frame. */ ++ uint16_t usecs; ++ ++ /** Interval between transfers in (micro)frames. */ ++ uint16_t interval; ++ ++ /** ++ * (micro)frame to initialize a periodic transfer. The transfer ++ * executes in the following (micro)frame. ++ */ ++ uint16_t sched_frame; ++ ++ /** (micro)frame at which last start split was initialized. */ ++ uint16_t start_split_frame; ++ ++ /** @} */ ++ ++ /** ++ * Used instead of original buffer if ++ * it(physical address) is not dword-aligned. ++ */ ++ uint8_t *dw_align_buf; ++ dwc_dma_t dw_align_buf_dma; ++ ++ /** Entry for QH in either the periodic or non-periodic schedule. */ ++ dwc_list_link_t qh_list_entry; ++ ++ /** @name Descriptor DMA support */ ++ /** @{ */ ++ ++ /** Descriptor List. */ ++ dwc_otg_host_dma_desc_t *desc_list; ++ ++ /** Descriptor List physical address. */ ++ dwc_dma_t desc_list_dma; ++ ++ /** ++ * Xfer Bytes array. ++ * Each element corresponds to a descriptor and indicates ++ * original XferSize size value for the descriptor. ++ */ ++ uint32_t *n_bytes; ++ ++ /** Actual number of transfer descriptors in a list. */ ++ uint16_t ntd; ++ ++ /** First activated isochronous transfer descriptor index. */ ++ uint8_t td_first; ++ /** Last activated isochronous transfer descriptor index. */ ++ uint8_t td_last; ++ ++ /** @} */ ++ ++} dwc_otg_qh_t; ++ ++DWC_CIRCLEQ_HEAD(hc_list, dwc_hc); ++ ++#ifdef HW2937_WORKAROUND ++ ++typedef enum { ++ HW2937_XFER_MODE_IDLE, ++ HW2937_XFER_MODE_IN, ++ HW2937_XFER_MODE_OUT, ++ HW2937_XFER_MODE_PAUSEIN /* Transitioning from IN to IDLE */ ++} hw2937_xfer_mode_t; ++#endif ++ ++/** ++ * This structure holds the state of the HCD, including the non-periodic and ++ * periodic schedules. ++ */ ++struct dwc_otg_hcd { ++ /** DWC OTG Core Interface Layer */ ++ dwc_otg_core_if_t *core_if; ++ ++ /** Function HCD driver callbacks */ ++ struct dwc_otg_hcd_function_ops *fops; ++ ++ /** Internal DWC HCD Flags */ ++ volatile union dwc_otg_hcd_internal_flags { ++ uint32_t d32; ++ struct { ++ unsigned port_connect_status_change:1; ++ unsigned port_connect_status:1; ++ unsigned port_reset_change:1; ++ unsigned port_enable_change:1; ++ unsigned port_suspend_change:1; ++ unsigned port_over_current_change:1; ++ unsigned port_l1_change:1; ++ unsigned reserved:26; ++ } b; ++ } flags; ++ ++ /** ++ * Inactive items in the non-periodic schedule. This is a list of ++ * Queue Heads. Transfers associated with these Queue Heads are not ++ * currently assigned to a host channel. ++ */ ++ dwc_list_link_t non_periodic_sched_inactive; ++ ++ /** ++ * Active items in the non-periodic schedule. This is a list of ++ * Queue Heads. Transfers associated with these Queue Heads are ++ * currently assigned to a host channel. ++ */ ++ dwc_list_link_t non_periodic_sched_active; ++ ++ /** ++ * Pointer to the next Queue Head to process in the active ++ * non-periodic schedule. ++ */ ++ dwc_list_link_t *non_periodic_qh_ptr; ++ ++ /** ++ * Inactive items in the periodic schedule. This is a list of QHs for ++ * periodic transfers that are _not_ scheduled for the next frame. ++ * Each QH in the list has an interval counter that determines when it ++ * needs to be scheduled for execution. This scheduling mechanism ++ * allows only a simple calculation for periodic bandwidth used (i.e. ++ * must assume that all periodic transfers may need to execute in the ++ * same frame). However, it greatly simplifies scheduling and should ++ * be sufficient for the vast majority of OTG hosts, which need to ++ * connect to a small number of peripherals at one time. ++ * ++ * Items move from this list to periodic_sched_ready when the QH ++ * interval counter is 0 at SOF. ++ */ ++ dwc_list_link_t periodic_sched_inactive; ++ ++ /** ++ * List of periodic QHs that are ready for execution in the next ++ * frame, but have not yet been assigned to host channels. ++ * ++ * Items move from this list to periodic_sched_assigned as host ++ * channels become available during the current frame. ++ */ ++ dwc_list_link_t periodic_sched_ready; ++ ++ /** ++ * List of periodic QHs to be executed in the next frame that are ++ * assigned to host channels. ++ * ++ * Items move from this list to periodic_sched_queued as the ++ * transactions for the QH are queued to the DWC_otg controller. ++ */ ++ dwc_list_link_t periodic_sched_assigned; ++ ++ /** ++ * List of periodic QHs that have been queued for execution. ++ * ++ * Items move from this list to either periodic_sched_inactive or ++ * periodic_sched_ready when the channel associated with the transfer ++ * is released. If the interval for the QH is 1, the item moves to ++ * periodic_sched_ready because it must be rescheduled for the next ++ * frame. Otherwise, the item moves to periodic_sched_inactive. ++ */ ++ dwc_list_link_t periodic_sched_queued; ++ ++ /** ++ * Total bandwidth claimed so far for periodic transfers. This value ++ * is in microseconds per (micro)frame. The assumption is that all ++ * periodic transfers may occur in the same (micro)frame. ++ */ ++ uint16_t periodic_usecs; ++ ++ /** ++ * Frame number read from the core at SOF. The value ranges from 0 to ++ * DWC_HFNUM_MAX_FRNUM. ++ */ ++ uint16_t frame_number; ++ ++ /** ++ * Free host channels in the controller. This is a list of ++ * dwc_hc_t items. ++ */ ++ struct hc_list free_hc_list; ++ /** ++ * Number of host channels assigned to periodic transfers. Currently ++ * assuming that there is a dedicated host channel for each periodic ++ * transaction and at least one host channel available for ++ * non-periodic transactions. ++ */ ++ int periodic_channels; ++ ++ /** ++ * Number of host channels assigned to non-periodic transfers. ++ */ ++ int non_periodic_channels; ++ ++ /** ++ * Array of pointers to the host channel descriptors. Allows accessing ++ * a host channel descriptor given the host channel number. This is ++ * useful in interrupt handlers. ++ */ ++ struct dwc_hc *hc_ptr_array[MAX_EPS_CHANNELS]; ++ ++ /** ++ * Buffer to use for any data received during the status phase of a ++ * control transfer. Normally no data is transferred during the status ++ * phase. This buffer is used as a bit bucket. ++ */ ++ uint8_t *status_buf; ++ ++ /** ++ * DMA address for status_buf. ++ */ ++ dma_addr_t status_buf_dma; ++#define DWC_OTG_HCD_STATUS_BUF_SIZE 64 ++ ++ /** ++ * Connection timer. An OTG host must display a message if the device ++ * does not connect. Started when the VBus power is turned on via ++ * sysfs attribute "buspower". ++ */ ++ dwc_timer_t *conn_timer; ++ ++ /* Tasket to do a reset */ ++ dwc_tasklet_t *reset_tasklet; ++ ++ /* */ ++ dwc_spinlock_t *lock; ++ ++ /** ++ * Private data that could be used by OS wrapper. ++ */ ++ void *priv; ++ ++ uint8_t otg_port; ++ ++ /** Frame List */ ++ uint32_t *frame_list; ++ ++ /** Frame List DMA address */ ++ dma_addr_t frame_list_dma; ++ ++#ifdef HW2937_WORKAROUND ++ /** Current transfer mode (IN, OUT, or IDLE) */ ++ hw2937_xfer_mode_t hw2937_xfer_mode; ++ ++ /** Mask of channels assigned to the current mode */ ++ uint32_t hw2937_assigned_channels; ++#endif ++ ++#ifdef DEBUG ++ uint32_t frrem_samples; ++ uint64_t frrem_accum; ++ ++ uint32_t hfnum_7_samples_a; ++ uint64_t hfnum_7_frrem_accum_a; ++ uint32_t hfnum_0_samples_a; ++ uint64_t hfnum_0_frrem_accum_a; ++ uint32_t hfnum_other_samples_a; ++ uint64_t hfnum_other_frrem_accum_a; ++ ++ uint32_t hfnum_7_samples_b; ++ uint64_t hfnum_7_frrem_accum_b; ++ uint32_t hfnum_0_samples_b; ++ uint64_t hfnum_0_frrem_accum_b; ++ uint32_t hfnum_other_samples_b; ++ uint64_t hfnum_other_frrem_accum_b; ++#endif ++}; ++ ++/** @name Transaction Execution Functions */ ++/** @{ */ ++extern dwc_otg_transaction_type_e dwc_otg_hcd_select_transactions(dwc_otg_hcd_t ++ * hcd); ++extern void dwc_otg_hcd_queue_transactions(dwc_otg_hcd_t * hcd, ++ dwc_otg_transaction_type_e tr_type); ++ ++/** @} */ ++ ++/** @name Interrupt Handler Functions */ ++/** @{ */ ++extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t * ++ dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t * ++ dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t * ++ dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_incomplete_periodic_intr(dwc_otg_hcd_t * ++ dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_conn_id_status_change_intr(dwc_otg_hcd_t * ++ dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_disconnect_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, ++ uint32_t num); ++extern int32_t dwc_otg_hcd_handle_session_req_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++extern int32_t dwc_otg_hcd_handle_wakeup_detected_intr(dwc_otg_hcd_t * ++ dwc_otg_hcd); ++/** @} */ ++ ++/** @name Schedule Queue Functions */ ++/** @{ */ ++ ++/* Implemented in dwc_otg_hcd_queue.c */ ++extern dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd, ++ dwc_otg_hcd_urb_t * urb); ++extern void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh); ++extern int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh); ++extern void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh); ++extern void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, ++ int sched_csplit); ++ ++/** Remove and free a QH */ ++static inline void dwc_otg_hcd_qh_remove_and_free(dwc_otg_hcd_t * hcd, ++ dwc_otg_qh_t * qh) ++{ ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ dwc_otg_hcd_qh_free(hcd, qh); ++} ++ ++/** Allocates memory for a QH structure. ++ * @return Returns the memory allocate or NULL on error. */ ++static inline dwc_otg_qh_t *dwc_otg_hcd_qh_alloc(void) ++{ ++ return (dwc_otg_qh_t *) dwc_alloc(sizeof(dwc_otg_qh_t)); ++} ++ ++extern dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(dwc_otg_hcd_urb_t * urb); ++extern void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb); ++extern int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, dwc_otg_hcd_t * dwc_otg_hcd, ++ dwc_otg_qh_t ** qh); ++ ++/** Allocates memory for a QTD structure. ++ * @return Returns the memory allocate or NULL on error. */ ++static inline dwc_otg_qtd_t *dwc_otg_hcd_qtd_alloc(void) ++{ ++ return (dwc_otg_qtd_t *) dwc_alloc(sizeof(dwc_otg_qtd_t)); ++} ++ ++/** Frees the memory for a QTD structure. QTD should already be removed from ++ * list. ++ * @param qtd QTD to free.*/ ++static inline void dwc_otg_hcd_qtd_free(dwc_otg_qtd_t * qtd) ++{ ++ dwc_free(qtd); ++} ++ ++/** Removes a QTD from list. ++ * @param hcd HCD instance. ++ * @param qtd QTD to remove from list. ++ * @param qh QTD belongs to. ++ */ ++static inline void dwc_otg_hcd_qtd_remove(dwc_otg_hcd_t * hcd, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_qh_t * qh) ++{ ++ uint64_t flags; ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ DWC_CIRCLEQ_REMOVE(&qh->qtd_list, qtd, qtd_list_entry); ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++} ++ ++/** Remove and free a QTD */ ++static inline void dwc_otg_hcd_qtd_remove_and_free(dwc_otg_hcd_t * hcd, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_qh_t * qh) ++{ ++ dwc_otg_hcd_qtd_remove(hcd, qtd, qh); ++ dwc_otg_hcd_qtd_free(qtd); ++} ++ ++/** @} */ ++ ++/** @name Descriptor DMA Supporting Functions */ ++/** @{ */ ++ ++extern void dwc_otg_hcd_start_xfer_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh); ++extern void dwc_otg_hcd_complete_xfer_ddma(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_halt_status_e halt_status); ++ ++extern int dwc_otg_hcd_qh_init_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh); ++extern void dwc_otg_hcd_qh_free_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh); ++ ++/** @} */ ++ ++/** @name Internal Functions */ ++/** @{ */ ++dwc_otg_qh_t *dwc_urb_to_qh(dwc_otg_hcd_urb_t * urb); ++/** @} */ ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++extern int dwc_otg_hcd_get_hc_for_lpm_tran(dwc_otg_hcd_t * hcd, ++ uint8_t devaddr); ++extern void dwc_otg_hcd_free_hc_from_lpm(dwc_otg_hcd_t * hcd); ++#endif ++ ++/** Gets the QH that contains the list_head */ ++#define dwc_list_to_qh(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qh_t, qh_list_entry) ++ ++/** Gets the QTD that contains the list_head */ ++#define dwc_list_to_qtd(_list_head_ptr_) container_of(_list_head_ptr_, dwc_otg_qtd_t, qtd_list_entry) ++ ++/** Check if QH is non-periodic */ ++#define dwc_qh_is_non_per(_qh_ptr_) ((_qh_ptr_->ep_type == UE_BULK) || \ ++ (_qh_ptr_->ep_type == UE_CONTROL)) ++ ++/** High bandwidth multiplier as encoded in highspeed endpoint descriptors */ ++#define dwc_hb_mult(wMaxPacketSize) (1 + (((wMaxPacketSize) >> 11) & 0x03)) ++ ++/** Packet size for any kind of endpoint descriptor */ ++#define dwc_max_packet(wMaxPacketSize) ((wMaxPacketSize) & 0x07ff) ++ ++/** ++ * Returns true if _frame1 is less than or equal to _frame2. The comparison is ++ * done modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the ++ * frame number when the max frame number is reached. ++ */ ++static inline int dwc_frame_num_le(uint16_t frame1, uint16_t frame2) ++{ ++ return ((frame2 - frame1) & DWC_HFNUM_MAX_FRNUM) <= ++ (DWC_HFNUM_MAX_FRNUM >> 1); ++} ++ ++/** ++ * Returns true if _frame1 is greater than _frame2. The comparison is done ++ * modulo DWC_HFNUM_MAX_FRNUM. This accounts for the rollover of the frame ++ * number when the max frame number is reached. ++ */ ++static inline int dwc_frame_num_gt(uint16_t frame1, uint16_t frame2) ++{ ++ return (frame1 != frame2) && ++ (((frame1 - frame2) & DWC_HFNUM_MAX_FRNUM) < ++ (DWC_HFNUM_MAX_FRNUM >> 1)); ++} ++ ++/** ++ * Increments _frame by the amount specified by _inc. The addition is done ++ * modulo DWC_HFNUM_MAX_FRNUM. Returns the incremented value. ++ */ ++static inline uint16_t dwc_frame_num_inc(uint16_t frame, uint16_t inc) ++{ ++ return (frame + inc) & DWC_HFNUM_MAX_FRNUM; ++} ++ ++static inline uint16_t dwc_full_frame_num(uint16_t frame) ++{ ++ return (frame & DWC_HFNUM_MAX_FRNUM) >> 3; ++} ++ ++static inline uint16_t dwc_micro_frame_num(uint16_t frame) ++{ ++ return frame & 0x7; ++} ++ ++void dwc_otg_hcd_save_data_toggle(dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd); ++ ++#ifdef DEBUG ++/** ++ * Macro to sample the remaining PHY clocks left in the current frame. This ++ * may be used during debugging to determine the average time it takes to ++ * execute sections of code. There are two possible sample points, "a" and ++ * "b", so the _letter argument must be one of these values. ++ * ++ * To dump the average sample times, read the "hcd_frrem" sysfs attribute. For ++ * example, "cat /sys/devices/lm0/hcd_frrem". ++ */ ++#define dwc_sample_frrem(_hcd, _qh, _letter) \ ++{ \ ++ hfnum_data_t hfnum; \ ++ dwc_otg_qtd_t *qtd; \ ++ qtd = list_entry(_qh->qtd_list.next, dwc_otg_qtd_t, qtd_list_entry); \ ++ if (usb_pipeint(qtd->urb->pipe) && _qh->start_split_frame != 0 && !qtd->complete_split) { \ ++ hfnum.d32 = dwc_read_reg32(&_hcd->core_if->host_if->host_global_regs->hfnum); \ ++ switch (hfnum.b.frnum & 0x7) { \ ++ case 7: \ ++ _hcd->hfnum_7_samples_##_letter++; \ ++ _hcd->hfnum_7_frrem_accum_##_letter += hfnum.b.frrem; \ ++ break; \ ++ case 0: \ ++ _hcd->hfnum_0_samples_##_letter++; \ ++ _hcd->hfnum_0_frrem_accum_##_letter += hfnum.b.frrem; \ ++ break; \ ++ default: \ ++ _hcd->hfnum_other_samples_##_letter++; \ ++ _hcd->hfnum_other_frrem_accum_##_letter += hfnum.b.frrem; \ ++ break; \ ++ } \ ++ } \ ++} ++#else ++#define dwc_sample_frrem(_hcd, _qh, _letter) ++#endif ++#endif ++#endif /* DWC_DEVICE_ONLY */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_ddma.c +@@ -0,0 +1,1106 @@ ++/*========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_ddma.c $ ++ * $Revision: #2 $ ++ * $Date: 2009/04/21 $ ++ * $Change: 1237473 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++/** @file ++ * This file contains Descriptor DMA support implementation for host mode. ++ */ ++ ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++ ++static inline uint8_t frame_list_idx(uint16_t frame) ++{ ++ return (frame & (MAX_FRLIST_EN_NUM - 1)); ++} ++ ++static inline uint16_t desclist_idx_inc(uint16_t idx, uint16_t inc, uint8_t speed) ++{ ++ return (idx + inc) & ++ (((speed == DWC_OTG_EP_SPEED_HIGH) ? MAX_DMA_DESC_NUM_HS_ISOC : MAX_DMA_DESC_NUM_GENERIC) - 1); ++} ++ ++static inline uint16_t desclist_idx_dec(uint16_t idx, uint16_t inc, uint8_t speed) ++{ ++ return (idx - inc) & ++ (((speed == DWC_OTG_EP_SPEED_HIGH) ? MAX_DMA_DESC_NUM_HS_ISOC : MAX_DMA_DESC_NUM_GENERIC) - 1); ++} ++ ++static inline uint16_t max_desc_num(dwc_otg_qh_t * qh) ++{ ++ return (((qh->ep_type == UE_ISOCHRONOUS) && (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH)) ++ ? ++ MAX_DMA_DESC_NUM_HS_ISOC ++ : ++ MAX_DMA_DESC_NUM_GENERIC); ++} ++static inline uint16_t frame_incr_val(dwc_otg_qh_t * qh) ++{ ++ return ((qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) ++ ? ((qh->interval + 8 - 1) / 8) ++ : ++ qh->interval); ++} ++ ++static int desc_list_alloc(dwc_otg_qh_t * qh) ++{ ++ int retval = 0; ++ ++ qh->desc_list = (dwc_otg_host_dma_desc_t *) ++ dwc_dma_alloc(sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh), ++ &qh->desc_list_dma ++ ); ++ ++ if (!qh->desc_list) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR("%s: DMA descriptor list allocation failed\n", __func__); ++ ++ } ++ ++ dwc_memset(qh->desc_list, 0x00, sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh)); ++ ++ ++ qh->n_bytes = (uint32_t *) dwc_alloc(sizeof(uint32_t) * max_desc_num(qh)); ++ ++ if (!qh->n_bytes) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR("%s: Failed to allocate array for descriptors' size actual values\n", ++ __func__); ++ ++ } ++ return retval; ++ ++} ++ ++static void desc_list_free(dwc_otg_qh_t * qh) ++{ ++ if(qh->desc_list) { ++ dwc_dma_free(max_desc_num(qh), qh->desc_list, qh->desc_list_dma); ++ qh->desc_list = NULL; ++ } ++ ++ if (qh->n_bytes) { ++ dwc_free(qh->n_bytes); ++ qh->n_bytes = NULL; ++ } ++} ++ ++static int frame_list_alloc(dwc_otg_hcd_t * hcd) ++{ ++ int retval = 0; ++ if (hcd->frame_list) ++ return 0; ++ ++ hcd->frame_list = dwc_dma_alloc(4 * MAX_FRLIST_EN_NUM, ++ &hcd->frame_list_dma ++ ); ++ if (!hcd->frame_list) { ++ retval = -DWC_E_NO_MEMORY; ++ DWC_ERROR("%s: Frame List allocation failed\n", __func__); ++ } ++ ++ dwc_memset(hcd->frame_list, 0x00, 4 * MAX_FRLIST_EN_NUM); ++ ++ return retval; ++} ++ ++static void frame_list_free(dwc_otg_hcd_t * hcd) ++{ ++ if (!hcd->frame_list) ++ return; ++ ++ dwc_dma_free(4 * MAX_FRLIST_EN_NUM, hcd->frame_list, hcd->frame_list_dma); ++ hcd->frame_list = NULL; ++} ++ ++static void per_sched_enable(dwc_otg_hcd_t * hcd, uint16_t fr_list_en) ++{ ++ ++ hcfg_data_t hcfg; ++ ++ hcfg.d32 = dwc_read_reg32(&hcd->core_if->host_if->host_global_regs->hcfg); ++ ++ if (hcfg.b.perschedstat) { ++ /* already enabled*/ ++ return; ++ } ++ ++ dwc_write_reg32(&hcd->core_if->host_if->host_global_regs->hflbaddr, hcd->frame_list_dma); ++ ++ switch(fr_list_en) { ++ case 64: ++ hcfg.b.frlisten = 3; ++ break; ++ case 32: ++ hcfg.b.frlisten = 2; ++ break; ++ case 16: ++ hcfg.b.frlisten = 1; ++ case 8: ++ hcfg.b.frlisten = 0; ++ default: ++ break; ++ } ++ ++ hcfg.b.perschedena = 1; ++ ++ DWC_DEBUGPL(DBG_HCD, "Enabling Periodic schedule\n"); ++ dwc_write_reg32(&hcd->core_if->host_if->host_global_regs->hcfg, hcfg.d32); ++ ++} ++ ++static void per_sched_disable(dwc_otg_hcd_t * hcd) ++{ ++ hcfg_data_t hcfg; ++ ++ hcfg.d32 = dwc_read_reg32(&hcd->core_if->host_if->host_global_regs->hcfg); ++ ++ if (!hcfg.b.perschedstat) { ++ /* already disabled */ ++ return; ++ } ++ hcfg.b.perschedena = 0; ++ ++ DWC_DEBUGPL(DBG_HCD, "Disabling Periodic schedule\n"); ++ dwc_write_reg32(&hcd->core_if->host_if->host_global_regs->hcfg, hcfg.d32); ++} ++ ++/* ++ * Activates/Deactivates FrameList entries for the channel ++ * based on endpoint servicing period. ++ */ ++void update_frame_list(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, uint8_t enable) ++{ ++ uint16_t i, j, inc; ++ dwc_hc_t *hc = qh->channel; ++ ++ inc = frame_incr_val(qh); ++ ++ if (qh->ep_type == UE_ISOCHRONOUS) ++ i = frame_list_idx(qh->sched_frame); ++ else ++ i = 0; ++ ++ j = i; ++ do { ++ if (enable) ++ hcd->frame_list[j] |= (1 << hc->hc_num); ++ else ++ hcd->frame_list[j] &= ~(1 << hc->hc_num); ++ j = (j + inc) & (MAX_FRLIST_EN_NUM - 1); ++ } ++ while (j != i); ++ ++ if (!enable) ++ return; ++ ++ hc->schinfo = 0; ++ if (qh->channel->speed == DWC_OTG_EP_SPEED_HIGH) { ++ j = 1; ++ for (i = 0 ; i < 8 / qh->interval; i++) { ++ hc->schinfo |= j; ++ j = j << qh->interval; ++ } ++ } ++ else { ++ hc->schinfo = 0xff; ++ } ++} ++#if 1 ++void dump_frame_list(dwc_otg_hcd_t * hcd) ++{ ++ int i = 0; ++ DWC_PRINTF("--FRAME LIST (hex) --\n"); ++ for (i = 0; i < MAX_FRLIST_EN_NUM; i++) { ++ DWC_PRINTF("%x\t",hcd->frame_list[i]); ++ if (!(i % 8) && i) ++ DWC_PRINTF("\n"); ++ } ++ DWC_PRINTF("\n----\n"); ++ ++} ++#endif ++ ++static void release_channel_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ dwc_hc_t *hc = qh->channel; ++ if (dwc_qh_is_non_per(qh)) { ++ hcd->non_periodic_channels--; ++ } ++ else { ++ update_frame_list(hcd, qh, 0); ++ } ++ /* ++ * The condition is added to prevent double cleanup try in case of device ++ * disconnect. See channel cleanup in dwc_otg_hcd_disconnect_cb(). ++ */ ++ if (hc->qh) { ++ dwc_otg_hc_cleanup(hcd->core_if, hc); ++ DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry); ++ hc->qh = NULL; ++ } ++ ++ qh->channel = NULL; ++ qh->ntd = 0; ++ ++ if (qh->desc_list) { ++ dwc_memset(qh->desc_list, 0x00, ++ sizeof(dwc_otg_host_dma_desc_t) * max_desc_num(qh)); ++ } ++} ++ ++/** ++ * Initializes a QH structure's Descriptor DMA related members. ++ * Allocates memory for descriptor list. ++ * On first periodic QH, allocates memory for FrameList ++ * and enables periodic scheduling. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh The QH to init. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++int dwc_otg_hcd_qh_init_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ int retval = 0; ++ ++ if (qh->do_split) { ++ DWC_ERROR("SPLIT Transfers are not supported in Descriptor DMA.\n"); ++ return -1; ++ } ++ ++ retval = desc_list_alloc(qh); ++ ++ if ((retval == 0) && (qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT)) { ++ if(!hcd->frame_list) { ++ retval = frame_list_alloc(hcd); ++ /* Enable periodic schedule on first periodic QH */ ++ if (retval == 0) ++ per_sched_enable(hcd, MAX_FRLIST_EN_NUM); ++ } ++ } ++ ++ qh->ntd = 0; ++ ++ return retval; ++} ++ ++/** ++ * Frees descriptor list memory associated with the QH. ++ * If QH is periodic and the last, frees FrameList memory ++ * and disables periodic scheduling. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh The QH to init. ++ */ ++void dwc_otg_hcd_qh_free_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ desc_list_free(qh); ++ ++ /* ++ * Channel still assigned due to some reasons. ++ * Seen on Isoc URB dequeue. Channel halted but no subsequent ++ * ChHalted interrupt to release the channel. Afterwards ++ * when it comes here from endpoint disable routine ++ * channel remains assigned. ++ */ ++ if (qh->channel) ++ release_channel_ddma(hcd, qh); ++ ++ if ((qh->ep_type == UE_ISOCHRONOUS || qh->ep_type == UE_INTERRUPT) ++ && !hcd->periodic_channels && hcd->frame_list) { ++ ++ per_sched_disable(hcd); ++ frame_list_free(hcd); ++ } ++} ++ ++static uint8_t frame_to_desc_idx(dwc_otg_qh_t * qh, uint16_t frame_idx) ++{ ++ if (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) { ++ /* ++ * Descriptor set(8 descriptors) index ++ * which is 8-aligned. ++ */ ++ return (frame_idx & ((MAX_DMA_DESC_NUM_HS_ISOC / 8) - 1)) * 8; ++ } ++ else { ++ return (frame_idx & (MAX_DMA_DESC_NUM_GENERIC - 1)); ++ } ++} ++ ++/* ++ * Determine starting frame for Isochronous transfer. ++ * Few frames skipped to prevent race condition with HC. ++ */ ++static uint8_t calc_starting_frame(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, uint8_t* skip_frames) ++{ ++ uint16_t frame = 0; ++ hcd->frame_number = dwc_otg_hcd_get_frame_number(hcd); ++ ++ /* sched_frame is always frame number(not uFrame) both in FS and HS !! */ ++ ++ /* ++ * skip_frames is used to limit activated descriptors number ++ * to avoid the situation when HC services the last activated ++ * descriptor firstly. ++ * Example for FS: ++ * Current frame is 1, scheduled frame is 3. Since HC always fetches the descriptor ++ * corresponding to curr_frame+1, the descriptor corresponding to frame 2 ++ * will be fetched. If the number of descriptors is max=64 (or greather) the list will ++ * be fully programmed with Active descriptors and it is possible case(rare) that the latest ++ * descriptor(considering rollback) corresponding to frame 2 will be serviced first. ++ * HS case is more probable because, in fact, up to 11 uframes(16 in the code) ++ * may be skipped. ++ */ ++ if (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) { ++ /* ++ * Consider uframe counter also, to start xfer asap. ++ * If half of the frame elapsed skip 2 frames otherwise ++ * just 1 frame. ++ * Starting descriptor index must be 8-aligned, so ++ * if the current frame is near to complete the next one ++ * is skipped as well. ++ */ ++ ++ if (dwc_micro_frame_num(hcd->frame_number) >= 5) { ++ *skip_frames = 2 * 8; ++ frame = dwc_frame_num_inc(hcd->frame_number, *skip_frames); ++ } ++ else { ++ *skip_frames = 1 * 8; ++ frame = dwc_frame_num_inc(hcd->frame_number, *skip_frames); ++ } ++ ++ frame = dwc_full_frame_num(frame); ++ } else { ++ /* ++ * Two frames are skipped for FS - the current and the next. ++ * But for descriptor programming, 1 frame(descriptor) is enough, ++ * see example above. ++ */ ++ *skip_frames = 1; ++ frame = dwc_frame_num_inc(hcd->frame_number, 2); ++ } ++ ++ return frame; ++} ++/* ++ * Calculate initial descriptor index for isochronous transfer ++ * based on scheduled frame. ++ */ ++static uint8_t recalc_initial_desc_idx(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ uint16_t frame = 0, fr_idx, fr_idx_tmp; ++ uint8_t skip_frames = 0 ; ++ /* ++ * With current ISOC processing algorithm the channel is being ++ * released when no more QTDs in the list(qh->ntd == 0). ++ * Thus this function is called only when qh->ntd == 0 and qh->channel == 0. ++ * ++ * So qh->channel != NULL branch is not used and just not removed from the ++ * source file. It is required for another possible approach which is, ++ * do not disable and release the channel when ISOC session completed, ++ * just move QH to inactive schedule until new QTD arrives. ++ * On new QTD, the QH moved back to 'ready' schedule, ++ * starting frame and therefore starting desc_index are recalculated. ++ * In this case channel is released only on ep_disable. ++ */ ++ ++ /* Calculate starting descriptor index. For INTERRUPT endpoint it is always 0. */ ++ if (qh->channel) { ++ frame = calc_starting_frame(hcd, qh, &skip_frames); ++ /* ++ * Calculate initial descriptor index based on FrameList current bitmap ++ * and servicing period. ++ */ ++ fr_idx_tmp = frame_list_idx(frame); ++ fr_idx = (MAX_FRLIST_EN_NUM + frame_list_idx(qh->sched_frame) - fr_idx_tmp) ++ % frame_incr_val(qh); ++ fr_idx = (fr_idx + fr_idx_tmp) % MAX_FRLIST_EN_NUM; ++ } ++ else { ++ qh->sched_frame = calc_starting_frame(hcd, qh, &skip_frames); ++ fr_idx = frame_list_idx(qh->sched_frame); ++ } ++ ++ qh->td_first = qh->td_last = frame_to_desc_idx(qh, fr_idx); ++ ++ return skip_frames; ++} ++ ++#define ISOC_URB_GIVEBACK_ASAP ++ ++#define MAX_ISOC_XFER_SIZE_FS 1023 ++#define MAX_ISOC_XFER_SIZE_HS 3072 ++#define DESCNUM_THRESHOLD 4 ++ ++static void init_isoc_dma_desc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, uint8_t skip_frames) ++{ ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc; ++ dwc_otg_qtd_t *qtd; ++ dwc_otg_host_dma_desc_t *dma_desc; ++ uint16_t idx, inc, n_desc, ntd_max, max_xfer_size; ++ ++ idx = qh->td_last; ++ inc = qh->interval; ++ n_desc = 0; ++ ++ ntd_max = (max_desc_num(qh) + qh->interval - 1) / qh->interval; ++ if (skip_frames && !qh->channel) ++ ntd_max = ntd_max - skip_frames / qh->interval; ++ ++ max_xfer_size = (qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) ? MAX_ISOC_XFER_SIZE_HS ++ : MAX_ISOC_XFER_SIZE_FS; ++ ++ DWC_CIRCLEQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) { ++ while ((qh->ntd < ntd_max) && (qtd->isoc_frame_index_last < qtd->urb->packet_count)) { ++ ++ dma_desc = &qh->desc_list[idx]; ++ dwc_memset(dma_desc, 0x00, sizeof(dwc_otg_host_dma_desc_t)); ++ ++ frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index_last]; ++ ++ if (frame_desc->length > max_xfer_size) ++ qh->n_bytes[idx] = max_xfer_size; ++ else ++ qh->n_bytes[idx] = frame_desc->length; ++ dma_desc->status.b_isoc.n_bytes = qh->n_bytes[idx]; ++ dma_desc->status.b_isoc.a = 1; ++ ++ dma_desc->buf = qtd->urb->dma + frame_desc->offset; ++ ++ qh->ntd++; ++ ++ qtd->isoc_frame_index_last++; ++ ++ #ifdef ISOC_URB_GIVEBACK_ASAP ++ /* ++ * Set IOC for each descriptor corresponding to the ++ * last frame of the URB. ++ */ ++ if (qtd->isoc_frame_index_last == qtd->urb->packet_count) ++ dma_desc->status.b_isoc.ioc = 1; ++ ++ #endif ++ idx = desclist_idx_inc(idx, inc, qh->dev_speed); ++ n_desc++; ++ ++ } ++ qtd->in_process = 1; ++ } ++ ++ qh->td_last = idx; ++ ++#ifdef ISOC_URB_GIVEBACK_ASAP ++ /* Set IOC for the last descriptor if descriptor list is full */ ++ if (qh->ntd == ntd_max) { ++ idx = desclist_idx_dec(qh->td_last, inc, qh->dev_speed); ++ qh->desc_list[idx].status.b_isoc.ioc = 1; ++ } ++#else ++ /* ++ * Set IOC bit only for one descriptor. ++ * Always try to be ahead of HW processing, ++ * i.e. on IOC generation driver activates next descriptors but ++ * core continues to process descriptors followed the one with IOC set. ++ */ ++ ++ if (n_desc > DESCNUM_THRESHOLD) { ++ /* ++ * Move IOC "up". Required even if there is only one QTD ++ * in the list, cause QTDs migth continue to be queued, ++ * but during the activation it was only one queued. ++ * Actually more than one QTD might be in the list if this function called ++ * from XferCompletion - QTDs was queued during HW processing of the previous ++ * descriptor chunk. ++ */ ++ idx = dwc_desclist_idx_dec(idx, inc * ((qh->ntd + 1) / 2), qh->dev_speed); ++ } ++ else { ++ /* ++ * Set the IOC for the latest descriptor ++ * if either number of descriptor is not greather than threshold ++ * or no more new descriptors activated. ++ */ ++ idx = dwc_desclist_idx_dec(qh->td_last, inc, qh->dev_speed); ++ } ++ ++ qh->desc_list[idx].status.b_isoc.ioc = 1; ++#endif ++} ++ ++ ++static void init_non_isoc_dma_desc(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ ++ dwc_hc_t *hc; ++ dwc_otg_host_dma_desc_t *dma_desc; ++ dwc_otg_qtd_t *qtd; ++ int num_packets, len, n_desc = 0; ++ ++ hc = qh->channel; ++ ++ /* ++ * Start with hc->xfer_buff initialized in ++ * assign_and_init_hc(), then if SG transfer consists of multiple URBs, ++ * this pointer re-assigned to the buffer of the currently processed QTD. ++ * For non-SG request there is always one QTD active. ++ */ ++ ++ DWC_CIRCLEQ_FOREACH(qtd, &qh->qtd_list, qtd_list_entry) { ++ ++ if (n_desc) { ++ /* SG request - more than 1 QTDs */ ++ hc->xfer_buff = (uint8_t *)qtd->urb->dma + qtd->urb->actual_length; ++ hc->xfer_len = qtd->urb->length - qtd->urb->actual_length; ++ } ++ ++ qtd->n_desc = 0; ++ ++ do { ++ dma_desc = &qh->desc_list[n_desc]; ++ len = hc->xfer_len; ++ ++ ++ if (len > MAX_DMA_DESC_SIZE) ++ len = MAX_DMA_DESC_SIZE - hc->max_packet + 1; ++ ++ if (hc->ep_is_in) { ++ if (len > 0) { ++ num_packets = (len + hc->max_packet - 1) / hc->max_packet; ++ } ++ else { ++ /* Need 1 packet for transfer length of 0. */ ++ num_packets = 1; ++ } ++ /* Always program an integral # of max packets for IN transfers. */ ++ len = num_packets * hc->max_packet; ++ } ++ ++ dma_desc->status.b.n_bytes = len; ++ ++ qh->n_bytes[n_desc] = len; ++ ++ ++ if ((qh->ep_type == UE_CONTROL) && (qtd->control_phase == DWC_OTG_CONTROL_SETUP)) ++ dma_desc->status.b.sup = 1; /* Setup Packet */ ++ ++ dma_desc->status.b.a = 1; /* Active descriptor */ ++ ++ dma_desc->buf = (uint32_t) hc->xfer_buff; ++ ++ /* ++ * Last descriptor(or single) of IN transfer ++ * with actual size less than MaxPacket. ++ */ ++ if (len > hc->xfer_len) { ++ hc->xfer_len = 0; ++ } ++ else { ++ hc->xfer_buff += len; ++ hc->xfer_len -= len; ++ } ++ ++ qtd->n_desc++; ++ n_desc++; ++ } ++ while ((hc->xfer_len > 0) && (n_desc != MAX_DMA_DESC_NUM_GENERIC)); ++ ++ ++ qtd->in_process = 1; ++ ++ if (n_desc == MAX_DMA_DESC_NUM_GENERIC) ++ break; ++ } ++ ++ if (n_desc) { ++ /* Request Transfer Complete interrupt for the last descriptor */ ++ qh->desc_list[n_desc-1].status.b.ioc = 1; ++ /* End of List indicator */ ++ qh->desc_list[n_desc-1].status.b.eol = 1; ++ ++ hc->ntd = n_desc; ++ } ++} ++ ++/** ++ * For Control and Bulk endpoints initializes descriptor list ++ * and starts the transfer. ++ * ++ * For Interrupt and Isochronous endpoints initializes descriptor list ++ * then updates FrameList, marking appropriate entries as active. ++ * In case of Isochronous, the starting descriptor index is calculated based ++ * on the scheduled frame, but only on the first transfer descriptor within a session. ++ * Then starts the transfer via enabling the channel. ++ * For Isochronous endpoint the channel is not halted on XferComplete ++ * interrupt so remains assigned to the endpoint(QH) until session is done. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh The QH to init. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++void dwc_otg_hcd_start_xfer_ddma(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ /* Channel is already assigned */ ++ dwc_hc_t *hc = qh->channel; ++ uint8_t skip_frames = 0; ++ ++ switch (hc->ep_type) { ++ case DWC_OTG_EP_TYPE_CONTROL: ++ case DWC_OTG_EP_TYPE_BULK: ++ init_non_isoc_dma_desc(hcd, qh); ++ ++ dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc); ++ break; ++ case DWC_OTG_EP_TYPE_INTR: ++ init_non_isoc_dma_desc(hcd, qh); ++ ++ update_frame_list(hcd, qh, 1); ++ ++ dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc); ++ break; ++ case DWC_OTG_EP_TYPE_ISOC: ++ ++ if(!qh->ntd) ++ skip_frames = recalc_initial_desc_idx(hcd, qh); ++ ++ init_isoc_dma_desc(hcd, qh, skip_frames); ++ ++ if (!hc->xfer_started) { ++ ++ update_frame_list(hcd, qh, 1); ++ ++ /* ++ * Always set to max, instead of actual size. ++ * Otherwise ntd will be changed with ++ * channel being enabled. Not recommended. ++ * ++ */ ++ hc->ntd = max_desc_num(qh); ++ /* Enable channel only once for ISOC */ ++ dwc_otg_hc_start_transfer_ddma(hcd->core_if, hc); ++ } ++ ++ break; ++ default: ++ ++ break; ++ } ++} ++ ++static void complete_isoc_xfer_ddma(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_halt_status_e halt_status) ++{ ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc; ++ dwc_otg_qtd_t *qtd, *qtd_tmp; ++ dwc_otg_qh_t *qh; ++ dwc_otg_host_dma_desc_t *dma_desc; ++ uint16_t idx, remain; ++ uint8_t urb_compl; ++ ++ qh = hc->qh; ++ idx = qh->td_first; ++ ++ ++ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) { ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) ++ qtd->in_process = 0; ++ return; ++ } ++ else if ((halt_status == DWC_OTG_HC_XFER_AHB_ERR) || ++ (halt_status == DWC_OTG_HC_XFER_BABBLE_ERR)) { ++ /* ++ * Channel is halted in these error cases. ++ * Considered as serious issues. ++ * Complete all URBs marking all frames as failed, ++ * irrespective whether some of the descriptors(frames) succeeded or no. ++ * Pass error code to completion routine as well, to ++ * update urb->status, some of class drivers might use it to stop ++ * queing transfer requests. ++ */ ++ int err = (halt_status == DWC_OTG_HC_XFER_AHB_ERR) ++ ? (-DWC_E_IO) ++ : (-DWC_E_OVERFLOW); ++ ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) { ++ for(idx = 0; idx < qtd->urb->packet_count; idx++) { ++ frame_desc = &qtd->urb->iso_descs[idx]; ++ frame_desc->status = err; ++ } ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, err); ++ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); ++ } ++ return; ++ } ++ ++ ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) { ++ ++ if (!qtd->in_process) ++ break; ++ ++ urb_compl = 0; ++ ++ do { ++ ++ dma_desc = &qh->desc_list[idx]; ++ ++ frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ remain = hc->ep_is_in ? dma_desc->status.b_isoc.n_bytes : 0; ++ ++ if (dma_desc->status.b_isoc.sts == DMA_DESC_STS_PKTERR) { ++ /* ++ * XactError or, unable to complete all the transactions ++ * in the scheduled micro-frame/frame, ++ * both indicated by DMA_DESC_STS_PKTERR. ++ */ ++ qtd->urb->error_count++; ++ frame_desc->actual_length = qh->n_bytes[idx] - remain; ++ frame_desc->status = -DWC_E_PROTOCOL; ++ } ++ else { ++ /* Success */ ++ ++ frame_desc->actual_length = qh->n_bytes[idx] - remain; ++ frame_desc->status = 0; ++ } ++ ++ if (++qtd->isoc_frame_index == qtd->urb->packet_count) { ++ /* ++ * urb->status is not used for isoc transfers here. ++ * The individual frame_desc status are used instead. ++ */ ++ ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); ++ ++ /* ++ * This check is necessary because urb_dequeue can be called ++ * from urb complete callback(sound driver example). ++ * All pending URBs are dequeued there, so no need for ++ * further processing. ++ */ ++ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) { ++ return; ++ } ++ ++ urb_compl = 1; ++ ++ } ++ ++ qh->ntd--; ++ ++ /* Stop if IOC requested descriptor reached */ ++ if (dma_desc->status.b_isoc.ioc) { ++ idx = desclist_idx_inc(idx, qh->interval, hc->speed); ++ goto stop_scan; ++ } ++ ++ idx = desclist_idx_inc(idx, qh->interval, hc->speed); ++ ++ if (urb_compl) ++ break; ++ } ++ while(idx != qh->td_first); ++ } ++stop_scan: ++ qh->td_first = idx; ++} ++ ++uint8_t update_non_isoc_urb_state_ddma(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_host_dma_desc_t * dma_desc, ++ dwc_otg_halt_status_e halt_status, ++ uint32_t n_bytes, ++ uint8_t *xfer_done) ++{ ++ ++ uint16_t remain = hc->ep_is_in ? dma_desc->status.b.n_bytes : 0; ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ ++ ++ if (halt_status == DWC_OTG_HC_XFER_AHB_ERR) { ++ urb->status = -DWC_E_IO; ++ return 1; ++ } ++ if (dma_desc->status.b.sts == DMA_DESC_STS_PKTERR) { ++ switch (halt_status) { ++ case DWC_OTG_HC_XFER_STALL: ++ urb->status = -DWC_E_PIPE; ++ break; ++ case DWC_OTG_HC_XFER_BABBLE_ERR: ++ urb->status = -DWC_E_OVERFLOW; ++ break; ++ case DWC_OTG_HC_XFER_XACT_ERR: ++ urb->status = -DWC_E_PROTOCOL; ++ break; ++ default: ++ DWC_ERROR("%s: Unhandled descriptor error status (%d)\n", __func__, ++ halt_status); ++ break; ++ } ++ return 1; ++ } ++ ++ if (dma_desc->status.b.a == 1) { ++ DWC_DEBUGPL(DBG_HCDV, "Active descriptor encountered on channel %d\n", hc->hc_num); ++ return 0; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL) { ++ if (qtd->control_phase == DWC_OTG_CONTROL_DATA) { ++ urb->actual_length += n_bytes - remain; ++ if (remain || urb->actual_length == urb->length) { ++ /* ++ * For Control Data stage do not set urb->status=0 to prevent ++ * URB callback. Set it when Status phase done. See below. ++ */ ++ *xfer_done = 1; ++ } ++ ++ } ++ else if (qtd->control_phase == DWC_OTG_CONTROL_STATUS) { ++ urb->status = 0; ++ *xfer_done = 1; ++ } ++ /* No handling for SETUP stage */ ++ ++ } ++ else { ++ /* BULK and INTR */ ++ urb->actual_length += n_bytes - remain; ++ if (remain || urb->actual_length == urb->length) { ++ urb->status = 0; ++ *xfer_done = 1; ++ } ++ } ++ ++ return 0; ++} ++ ++static void complete_non_isoc_xfer_ddma(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_halt_status_e halt_status) ++{ ++ dwc_otg_hcd_urb_t *urb = NULL; ++ dwc_otg_qtd_t *qtd, *qtd_tmp; ++ dwc_otg_qh_t *qh; ++ dwc_otg_host_dma_desc_t *dma_desc; ++ uint32_t n_bytes, n_desc, i; ++ uint8_t failed = 0, xfer_done; ++ ++ n_desc = 0; ++ ++ qh = hc->qh; ++ ++ ++ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE) { ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &hc->qh->qtd_list, qtd_list_entry) { ++ qtd->in_process = 0; ++ } ++ return; ++ } ++ ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) { ++ ++ urb = qtd->urb; ++ ++ n_bytes = 0; ++ xfer_done = 0; ++ ++ for (i = 0; i < qtd->n_desc; i++) { ++ dma_desc = &qh->desc_list[n_desc]; ++ ++ n_bytes = qh->n_bytes[n_desc]; ++ ++ ++ failed = update_non_isoc_urb_state_ddma(hcd, hc, qtd, dma_desc, ++ halt_status, n_bytes, &xfer_done); ++ ++ if (failed || (xfer_done && (urb->status != -DWC_E_IN_PROGRESS))) { ++ ++ hcd->fops->complete(hcd, urb->priv, urb, urb->status); ++ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); ++ ++ if (failed) ++ goto stop_scan; ++ } ++ else if (qh->ep_type == UE_CONTROL) { ++ if (qtd->control_phase == DWC_OTG_CONTROL_SETUP) { ++ if (urb->length > 0) { ++ qtd->control_phase = DWC_OTG_CONTROL_DATA; ++ } else { ++ qtd->control_phase = DWC_OTG_CONTROL_STATUS; ++ } ++ DWC_DEBUGPL(DBG_HCDV, " Control setup transaction done\n"); ++ } ++ else if(qtd->control_phase == DWC_OTG_CONTROL_DATA) { ++ if (xfer_done) { ++ qtd->control_phase = DWC_OTG_CONTROL_STATUS; ++ DWC_DEBUGPL(DBG_HCDV, " Control data transfer done\n"); ++ } else if (i+1 == qtd->n_desc){ ++ /* ++ * Last descriptor for Control data stage which is ++ * not completed yet. ++ */ ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ } ++ } ++ } ++ ++ n_desc++; ++ } ++ ++ } ++ ++stop_scan: ++ ++ if (qh->ep_type != UE_CONTROL) { ++ /* ++ * Resetting the data toggle for bulk ++ * and interrupt endpoints in case of stall. See handle_hc_stall_intr() ++ */ ++ if (halt_status == DWC_OTG_HC_XFER_STALL) { ++ qh->data_toggle = DWC_OTG_HC_PID_DATA0; ++ } ++ else { ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ } ++ } ++ ++ if (halt_status == DWC_OTG_HC_XFER_COMPLETE) { ++ hcint_data_t hcint; ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ if (hcint.b.nyet) { ++ /* ++ * Got a NYET on the last transaction of the transfer. It ++ * means that the endpoint should be in the PING state at the ++ * beginning of the next transfer. ++ */ ++ qh->ping_state = 1; ++ clear_hc_int(hc_regs, nyet); ++ } ++ ++ } ++ ++} ++ ++/** ++ * This function is called from interrupt handlers. ++ * Scans the descriptor list, updates URB's status and ++ * calls completion routine for the URB if it's done. ++ * Releases the channel to be used by other transfers. ++ * In case of Isochronous endpoint the channel is not halted until ++ * the end of the session, i.e. QTD list is empty. ++ * If periodic channel released the FrameList is updated accordingly. ++ * ++ * Calls transaction selection routines to activate pending transfers. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param hc Host channel, the transfer is completed on. ++ * @param hc_regs Host channel registers. ++ * @param halt_status Reason the channel is being halted, ++ * or just XferComplete for isochronous transfer ++ */ ++void dwc_otg_hcd_complete_xfer_ddma(dwc_otg_hcd_t *hcd, ++ dwc_hc_t *hc, ++ dwc_otg_hc_regs_t *hc_regs, ++ dwc_otg_halt_status_e halt_status) ++{ ++ uint8_t continue_isoc_xfer = 0; ++ dwc_otg_transaction_type_e tr_type; ++ dwc_otg_qh_t *qh = hc->qh; ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ ++ complete_isoc_xfer_ddma(hcd, hc, hc_regs, halt_status); ++ ++ /* Release the channel if halted or session completed */ ++ if (halt_status != DWC_OTG_HC_XFER_COMPLETE || ++ DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { ++ ++ /* Halt the channel if session completed */ ++ if (halt_status == DWC_OTG_HC_XFER_COMPLETE) { ++ dwc_otg_hc_halt(hcd->core_if, hc, halt_status); ++ } ++ ++ release_channel_ddma(hcd, qh); ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ } ++ else { ++ /* Keep in assigned schedule to continue transfer */ ++ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned, ++ &qh->qh_list_entry); ++ continue_isoc_xfer = 1; ++ ++ } ++ /** @todo Consider the case when period exceeds FrameList size. ++ * Frame Rollover interrupt should be used. ++ */ ++ } ++ else { ++ /* Scan descriptor list to complete the URB(s), then release the channel */ ++ complete_non_isoc_xfer_ddma(hcd, hc, hc_regs, halt_status); ++ ++ release_channel_ddma(hcd, qh); ++ ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ ++ if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { ++ /* Add back to inactive non-periodic schedule on normal completion */ ++ dwc_otg_hcd_qh_add(hcd, qh); ++ } ++ ++ ++ } ++ tr_type = dwc_otg_hcd_select_transactions(hcd); ++ if (tr_type != DWC_OTG_TRANSACTION_NONE || continue_isoc_xfer) { ++ if (continue_isoc_xfer) { ++ if (tr_type == DWC_OTG_TRANSACTION_NONE) { ++ tr_type = DWC_OTG_TRANSACTION_PERIODIC; ++ } else if (tr_type == DWC_OTG_TRANSACTION_NON_PERIODIC) { ++ tr_type = DWC_OTG_TRANSACTION_ALL; ++ } ++ } ++ dwc_otg_hcd_queue_transactions(hcd, tr_type); ++ } ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_if.h +@@ -0,0 +1,393 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_if.h $ ++ * $Revision: #6 $ ++ * $Date: 2009/04/21 $ ++ * $Change: 1237474 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++#ifndef __DWC_HCD_IF_H__ ++#define __DWC_HCD_IF_H__ ++ ++#include "dwc_otg_core_if.h" ++ ++/** @file ++ * This file defines DWC_OTG HCD Core API. ++ */ ++ ++struct dwc_otg_hcd; ++typedef struct dwc_otg_hcd dwc_otg_hcd_t; ++ ++struct dwc_otg_hcd_urb; ++typedef struct dwc_otg_hcd_urb dwc_otg_hcd_urb_t; ++ ++/** @name HCD Function Driver Callbacks */ ++/** @{ */ ++ ++/** This function is called whenever core switches to host mode. */ ++typedef int (*dwc_otg_hcd_start_cb_t) (dwc_otg_hcd_t * hcd); ++ ++/** This function is called when device has been disconnected */ ++typedef int (*dwc_otg_hcd_disconnect_cb_t) (dwc_otg_hcd_t * hcd); ++ ++/** Wrapper provides this function to HCD to core, so it can get hub information to which device is connected */ ++typedef int (*dwc_otg_hcd_hub_info_from_urb_cb_t) (dwc_otg_hcd_t * hcd, ++ void *urb_handle, ++ uint32_t * hub_addr, ++ uint32_t * port_addr); ++/** Via this function HCD core gets device speed */ ++typedef int (*dwc_otg_hcd_speed_from_urb_cb_t) (dwc_otg_hcd_t * hcd, ++ void *urb_handle); ++ ++/** This function is called when urb is completed */ ++typedef int (*dwc_otg_hcd_complete_urb_cb_t) (dwc_otg_hcd_t * hcd, ++ void *urb_handle, ++ dwc_otg_hcd_urb_t * dwc_otg_urb, ++ int32_t status); ++ ++/** Via this function HCD core gets b_hnp_enable parameter */ ++typedef int (*dwc_otg_hcd_get_b_hnp_enable) (dwc_otg_hcd_t * hcd); ++ ++struct dwc_otg_hcd_function_ops { ++ dwc_otg_hcd_start_cb_t start; ++ dwc_otg_hcd_disconnect_cb_t disconnect; ++ dwc_otg_hcd_hub_info_from_urb_cb_t hub_info; ++ dwc_otg_hcd_speed_from_urb_cb_t speed; ++ dwc_otg_hcd_complete_urb_cb_t complete; ++ dwc_otg_hcd_get_b_hnp_enable get_b_hnp_enable; ++}; ++/** @} */ ++ ++/** @name HCD Core API */ ++/** @{ */ ++/** This function allocates dwc_otg_hcd structure and returns pointer on it. */ ++extern dwc_otg_hcd_t *dwc_otg_hcd_alloc_hcd(void); ++ ++/** This function should be called to initiate HCD Core. ++ * ++ * @param hcd The HCD ++ * @param core_if The DWC_OTG Core ++ * ++ * Returns -DWC_E_NO_MEMORY if no enough memory. ++ * Returns 0 on success ++ */ ++extern int dwc_otg_hcd_init(dwc_otg_hcd_t * hcd, dwc_otg_core_if_t * core_if); ++ ++/** Frees HCD ++ * ++ * @param hcd The HCD ++ */ ++extern void dwc_otg_hcd_remove(dwc_otg_hcd_t * hcd); ++ ++/** This function should be called on every hardware interrupt. ++ * ++ * @param dwc_otg_hcd The HCD ++ * ++ * Returns non zero if interrupt is handled ++ * Return 0 if interrupt is not handled ++ */ ++extern int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd); ++ ++/** ++ * Returns private data set by ++ * dwc_otg_hcd_set_priv_data function. ++ * ++ * @param hcd The HCD ++ */ ++extern void *dwc_otg_hcd_get_priv_data(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Set private data. ++ * ++ * @param hcd The HCD ++ * @param priv_data pointer to be stored in private data ++ */ ++extern void dwc_otg_hcd_set_priv_data(dwc_otg_hcd_t * hcd, void *priv_data); ++ ++/** ++ * This function initializes the HCD Core. ++ * ++ * @param hcd The HCD ++ * @param fops The Function Driver Operations data structure containing pointers to all callbacks. ++ * ++ * Returns -DWC_E_NO_DEVICE if Core is currently is in device mode. ++ * Returns 0 on success ++ */ ++extern int dwc_otg_hcd_start(dwc_otg_hcd_t * hcd, ++ struct dwc_otg_hcd_function_ops *fops); ++ ++/** ++ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are ++ * stopped. ++ * ++ * @param hcd The HCD ++ */ ++extern void dwc_otg_hcd_stop(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Handles hub class-specific requests. ++ * ++ * @param dwc_otg_hcd The HCD ++ * @param typeReq Request Type ++ * @param wValue wValue from control request ++ * @param wIndex wIndex from control request ++ * @param buf data buffer ++ * @param wLength data buffer length ++ * ++ * Returns -DWC_E_INVALID if invalid argument is passed ++ * Returns 0 on success ++ */ ++extern int dwc_otg_hcd_hub_control(dwc_otg_hcd_t * dwc_otg_hcd, ++ uint16_t typeReq, uint16_t wValue, ++ uint16_t wIndex, uint8_t * buf, ++ uint16_t wLength); ++ ++/** ++ * Returns otg port number. ++ * ++ * @param hcd The HCD ++ */ ++extern uint32_t dwc_otg_hcd_otg_port(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Returns 1 if currently core is acting as B host, and 0 otherwise. ++ * ++ * @param hcd The HCD ++ */ ++extern uint32_t dwc_otg_hcd_is_b_host(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Returns current frame number. ++ * ++ * @param hcd The HCD ++ */ ++extern int dwc_otg_hcd_get_frame_number(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Dumps hcd state. ++ * ++ * @param hcd The HCD ++ */ ++extern void dwc_otg_hcd_dump_state(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Dump the average frame remaining at SOF. This can be used to ++ * determine average interrupt latency. Frame remaining is also shown for ++ * start transfer and two additional sample points. ++ * Currently this function is not implemented. ++ * ++ * @param hcd The HCD ++ */ ++extern void dwc_otg_hcd_dump_frrem(dwc_otg_hcd_t * hcd); ++ ++/** ++ * Sends LPM transaction to the local device. ++ * ++ * @param hcd The HCD ++ * @param devaddr Device Address ++ * @param hird Host initiated resume duration ++ * @param bRemoteWake Value of bRemoteWake field in LPM transaction ++ * ++ * Returns negative value if sending LPM transaction was not succeeded. ++ * Returns 0 on success. ++ */ ++extern int dwc_otg_hcd_send_lpm(dwc_otg_hcd_t * hcd, uint8_t devaddr, ++ uint8_t hird, uint8_t bRemoteWake); ++ ++/* URB interface */ ++ ++/** ++ * Allocates memory for dwc_otg_hcd_urb structure. ++ * Allocated memory should be freed by call dwc_free function. ++ * ++ * @param hcd The HCD ++ * @param iso_desc_count Count of ISOC descriptors ++ * @param atomic_alloc Specefies whether to perform atomic allocation. ++ */ ++extern dwc_otg_hcd_urb_t *dwc_otg_hcd_urb_alloc(dwc_otg_hcd_t * hcd, ++ int iso_desc_count, ++ int atomic_alloc); ++ ++/** ++ * Set pipe information in URB. ++ * ++ * @param hcd_urb DWC_OTG URB ++ * @param devaddr Device Address ++ * @param ep_num Endpoint Number ++ * @param ep_type Endpoint Type ++ * @param ep_dir Endpoint Direction ++ * @param mps Max Packet Size ++ */ ++extern void dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_hcd_urb_t * hcd_urb, ++ uint8_t devaddr, uint8_t ep_num, ++ uint8_t ep_type, uint8_t ep_dir, ++ uint16_t mps); ++ ++/* Transfer flags */ ++#define URB_GIVEBACK_ASAP 0x1 ++#define URB_SEND_ZERO_PACKET 0x2 ++ ++/** ++ * Sets dwc_otg_hcd_urb parameters. ++ * ++ * @param urb DWC_OTG URB allocated by dwc_otg_hcd_urb_alloc function. ++ * @param urb_handle Unique handle for request, this will be passed back ++ * to function driver in completion callback. ++ * @param buf The buffer for the data ++ * @param dma The DMA buffer for the data ++ * @param buflen Transfer length ++ * @param sp Buffer for setup data ++ * @param sp_dma DMA address of setup data buffer ++ * @param flags Transfer flags ++ * @param interval Polling interval for interrupt or isochronous transfers. ++ */ ++extern void dwc_otg_hcd_urb_set_params(dwc_otg_hcd_urb_t * urb, ++ void *urb_handle, void *buf, ++ dwc_dma_t dma, uint32_t buflen, void *sp, ++ dwc_dma_t sp_dma, uint32_t flags, ++ uint16_t interval); ++ ++/** Gets status from dwc_otg_hcd_urb ++ * ++ * @param dwc_otg_urb DWC_OTG URB ++ */ ++extern uint32_t dwc_otg_hcd_urb_get_status(dwc_otg_hcd_urb_t * dwc_otg_urb); ++ ++/** Gets actual length from dwc_otg_hcd_urb ++ * ++ * @param dwc_otg_urb DWC_OTG URB ++ */ ++extern uint32_t dwc_otg_hcd_urb_get_actual_length(dwc_otg_hcd_urb_t * ++ dwc_otg_urb); ++ ++/** Gets error count from dwc_otg_hcd_urb. Only for ISOC URBs ++ * ++ * @param dwc_otg_urb DWC_OTG URB ++ */ ++extern uint32_t dwc_otg_hcd_urb_get_error_count(dwc_otg_hcd_urb_t * ++ dwc_otg_urb); ++ ++/** Set ISOC descriptor offset and length ++ * ++ * @param dwc_otg_urb DWC_OTG URB ++ * @param desc_num ISOC descriptor number ++ * @param offset Offset from beginig of buffer. ++ * @param length Transaction length ++ */ ++extern void dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_hcd_urb_t * dwc_otg_urb, ++ int desc_num, uint32_t offset, ++ uint32_t length); ++ ++/** Get status of ISOC descriptor, specified by desc_num ++ * ++ * @param dwc_otg_urb DWC_OTG URB ++ * @param desc_num ISOC descriptor number ++ */ ++extern uint32_t dwc_otg_hcd_urb_get_iso_desc_status(dwc_otg_hcd_urb_t * ++ dwc_otg_urb, int desc_num); ++ ++/** Get actual length of ISOC descriptor, specified by desc_num ++ * ++ * @param dwc_otg_urb DWC_OTG URB ++ * @param desc_num ISOC descriptor number ++ */ ++extern uint32_t dwc_otg_hcd_urb_get_iso_desc_actual_length(dwc_otg_hcd_urb_t * ++ dwc_otg_urb, ++ int desc_num); ++ ++/** Queue URB. After transfer is completes, the complete callback will be called with the URB status ++ * ++ * @param dwc_otg_hcd The HCD ++ * @param dwc_otg_urb DWC_OTG URB ++ * @param ep_handle Out parameter for returning endpoint handle ++ * ++ * Returns -DWC_E_NO_DEVICE if no device is connected. ++ * Returns -DWC_E_NO_MEMORY if there is no enough memory. ++ * Returns 0 on success. ++ */ ++extern int dwc_otg_hcd_urb_enqueue(dwc_otg_hcd_t * dwc_otg_hcd, ++ dwc_otg_hcd_urb_t * dwc_otg_urb, ++ void **ep_handle); ++ ++/** De-queue the specified URB ++ * ++ * @param dwc_otg_hcd The HCD ++ * @param dwc_otg_urb DWC_OTG URB ++ */ ++extern int dwc_otg_hcd_urb_dequeue(dwc_otg_hcd_t * dwc_otg_hcd, ++ dwc_otg_hcd_urb_t * dwc_otg_urb); ++ ++/** Frees resources in the DWC_otg controller related to a given endpoint. ++ * Any URBs for the endpoint must already be dequeued. ++ * ++ * @param hcd The HCD ++ * @param ep_handle Endpoint handle, returned by dwc_otg_hcd_urb_enqueue function ++ * @param retry Number of retries if there are queued transfers. ++ * ++ * Returns -DWC_E_INVALID if invalid arguments are passed. ++ * Returns 0 on success ++ */ ++extern int dwc_otg_hcd_endpoint_disable(dwc_otg_hcd_t * hcd, void *ep_handle, ++ int retry); ++ ++/** Returns 1 if status of specified port is changed and 0 otherwise. ++ * ++ * @param hcd The HCD ++ * @param port Port number ++ */ ++extern int dwc_otg_hcd_is_status_changed(dwc_otg_hcd_t * hcd, int port); ++ ++/** Call this function to check if bandwidth was allocated for specified endpoint. ++ * Only for ISOC and INTERRUPT endpoints. ++ * ++ * @param hcd The HCD ++ * @param ep_handle Endpoint handle ++ */ ++extern int dwc_otg_hcd_is_bandwidth_allocated(dwc_otg_hcd_t * hcd, ++ void *ep_handle); ++ ++/** Call this function to check if bandwidth was freed for specified endpoint. ++ * ++ * @param hcd The HCD ++ * @param ep_handle Endpoint handle ++ */ ++extern int dwc_otg_hcd_is_bandwidth_freed(dwc_otg_hcd_t * hcd, void *ep_handle); ++ ++/** Returns bandwidth allocated for specified endpoint in microseconds. ++ * Only for ISOC and INTERRUPT endpoints. ++ * ++ * @param hcd The HCD ++ * @param ep_handle Endpoint handle ++ */ ++extern uint8_t dwc_otg_hcd_get_ep_bandwidth(dwc_otg_hcd_t * hcd, ++ void *ep_handle); ++ ++/** @} */ ++ ++#endif /* __DWC_HCD_IF_H__ */ ++#endif /* DWC_DEVICE_ONLY */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_intr.c +@@ -0,0 +1,2065 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_intr.c $ ++ * $Revision: #77 $ ++ * $Date: 2009/04/21 $ ++ * $Change: 1237475 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++/** @file ++ * This file contains the implementation of the HCD Interrupt handlers. ++ */ ++ ++/** This function handles interrupts for the HCD. */ ++int32_t dwc_otg_hcd_handle_intr(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ int retval = 0; ++ ++ dwc_otg_core_if_t *core_if = dwc_otg_hcd->core_if; ++ gintsts_data_t gintsts; ++#ifdef DEBUG ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ ++ //GRAYG: debugging ++ if (NULL == global_regs) { ++ DWC_DEBUGPL(DBG_HCD, "**** NULL regs: dwc_otg_hcd=%p " ++ "core_if=%p\n", ++ dwc_otg_hcd, global_regs); ++ return retval; ++ } ++#endif ++ ++ /* Check if HOST Mode */ ++ if (dwc_otg_is_host_mode(core_if)) { ++ gintsts.d32 = dwc_otg_read_core_intr(core_if); ++ if (!gintsts.d32) { ++ return 0; ++ } ++#ifdef DEBUG ++ /* Don't print debug message in the interrupt handler on SOF */ ++#ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++#endif ++ DWC_DEBUGPL(DBG_HCD, "\n"); ++#endif ++ ++#ifdef DEBUG ++#ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++#endif ++ DWC_DEBUGPL(DBG_HCD, ++ "DWC OTG HCD Interrupt Detected gintsts&gintmsk=0x%08x core_if=%p\n", ++ gintsts.d32, core_if); ++#endif ++ ++ if (gintsts.b.sofintr) { ++ retval |= dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd); ++ } ++ if (gintsts.b.rxstsqlvl) { ++ retval |= ++ dwc_otg_hcd_handle_rx_status_q_level_intr ++ (dwc_otg_hcd); ++ } ++ if (gintsts.b.nptxfempty) { ++ retval |= ++ dwc_otg_hcd_handle_np_tx_fifo_empty_intr ++ (dwc_otg_hcd); ++ } ++ if (gintsts.b.i2cintr) { ++ /** @todo Implement i2cintr handler. */ ++ } ++ if (gintsts.b.portintr) { ++ retval |= dwc_otg_hcd_handle_port_intr(dwc_otg_hcd); ++ } ++ if (gintsts.b.hcintr) { ++ retval |= dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd); ++ } ++ if (gintsts.b.ptxfempty) { ++ retval |= ++ dwc_otg_hcd_handle_perio_tx_fifo_empty_intr ++ (dwc_otg_hcd); ++ } ++#ifdef DEBUG ++#ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++#endif ++ { ++ DWC_DEBUGPL(DBG_HCD, ++ "DWC OTG HCD Finished Servicing Interrupts\n"); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintsts=0x%08x\n", ++ dwc_read_reg32(&global_regs->gintsts)); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD gintmsk=0x%08x\n", ++ dwc_read_reg32(&global_regs->gintmsk)); ++ } ++#endif ++ ++#ifdef DEBUG ++#ifndef DEBUG_SOF ++ if (gintsts.d32 != DWC_SOF_INTR_MASK) ++#endif ++ DWC_DEBUGPL(DBG_HCD, "\n"); ++#endif ++ ++ } ++ ++ return retval; ++} ++ ++#ifdef DWC_TRACK_MISSED_SOFS ++#warning Compiling code to track missed SOFs ++#define FRAME_NUM_ARRAY_SIZE 1000 ++/** ++ * This function is for debug only. ++ */ ++static inline void track_missed_sofs(uint16_t curr_frame_number) ++{ ++ static uint16_t frame_num_array[FRAME_NUM_ARRAY_SIZE]; ++ static uint16_t last_frame_num_array[FRAME_NUM_ARRAY_SIZE]; ++ static int frame_num_idx = 0; ++ static uint16_t last_frame_num = DWC_HFNUM_MAX_FRNUM; ++ static int dumped_frame_num_array = 0; ++ ++ if (frame_num_idx < FRAME_NUM_ARRAY_SIZE) { ++ if (((last_frame_num + 1) & DWC_HFNUM_MAX_FRNUM) != ++ curr_frame_number) { ++ frame_num_array[frame_num_idx] = curr_frame_number; ++ last_frame_num_array[frame_num_idx++] = last_frame_num; ++ } ++ } else if (!dumped_frame_num_array) { ++ int i; ++ DWC_PRINTF("Frame Last Frame\n"); ++ DWC_PRINTF("----- ----------\n"); ++ for (i = 0; i < FRAME_NUM_ARRAY_SIZE; i++) { ++ DWC_PRINTF("0x%04x 0x%04x\n", ++ frame_num_array[i], last_frame_num_array[i]); ++ } ++ dumped_frame_num_array = 1; ++ } ++ last_frame_num = curr_frame_number; ++} ++#endif ++ ++/** ++ * Handles the start-of-frame interrupt in host mode. Non-periodic ++ * transactions may be queued to the DWC_otg controller for the current ++ * (micro)frame. Periodic transactions may be queued to the controller for the ++ * next (micro)frame. ++ */ ++int32_t dwc_otg_hcd_handle_sof_intr(dwc_otg_hcd_t * hcd) ++{ ++ hfnum_data_t hfnum; ++ dwc_list_link_t *qh_entry; ++ dwc_otg_qh_t *qh; ++ dwc_otg_transaction_type_e tr_type; ++ gintsts_data_t gintsts = {.d32 = 0 }; ++ ++ hfnum.d32 = ++ dwc_read_reg32(&hcd->core_if->host_if->host_global_regs->hfnum); ++ ++#ifdef DEBUG_SOF ++ DWC_DEBUGPL(DBG_HCD, "--Start of Frame Interrupt--\n"); ++#endif ++ hcd->frame_number = hfnum.b.frnum; ++ ++#ifdef DEBUG ++ hcd->frrem_accum += hfnum.b.frrem; ++ hcd->frrem_samples++; ++#endif ++ ++#ifdef DWC_TRACK_MISSED_SOFS ++ track_missed_sofs(hcd->frame_number); ++#endif ++ /* Determine whether any periodic QHs should be executed. */ ++ qh_entry = DWC_LIST_FIRST(&hcd->periodic_sched_inactive); ++ while (qh_entry != &hcd->periodic_sched_inactive) { ++ qh = DWC_LIST_ENTRY(qh_entry, dwc_otg_qh_t, qh_list_entry); ++ qh_entry = qh_entry->next; ++ if (dwc_frame_num_le(qh->sched_frame, hcd->frame_number)) { ++ /* ++ * Move QH to the ready list to be executed next ++ * (micro)frame. ++ */ ++ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, ++ &qh->qh_list_entry); ++ } ++ } ++ tr_type = dwc_otg_hcd_select_transactions(hcd); ++ if (tr_type != DWC_OTG_TRANSACTION_NONE) { ++ dwc_otg_hcd_queue_transactions(hcd, tr_type); ++ } ++ ++ /* Clear interrupt */ ++ gintsts.b.sofintr = 1; ++ dwc_write_reg32(&hcd->core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** Handles the Rx Status Queue Level Interrupt, which indicates that there is at ++ * least one packet in the Rx FIFO. The packets are moved from the FIFO to ++ * memory if the DWC_otg controller is operating in Slave mode. */ ++int32_t dwc_otg_hcd_handle_rx_status_q_level_intr(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ host_grxsts_data_t grxsts; ++ dwc_hc_t *hc = NULL; ++ ++ DWC_DEBUGPL(DBG_HCD, "--RxStsQ Level Interrupt--\n"); ++ ++ grxsts.d32 = ++ dwc_read_reg32(&dwc_otg_hcd->core_if->core_global_regs->grxstsp); ++ ++ hc = dwc_otg_hcd->hc_ptr_array[grxsts.b.chnum]; ++ ++ /* Packet Status */ ++ DWC_DEBUGPL(DBG_HCDV, " Ch num = %d\n", grxsts.b.chnum); ++ DWC_DEBUGPL(DBG_HCDV, " Count = %d\n", grxsts.b.bcnt); ++ DWC_DEBUGPL(DBG_HCDV, " DPID = %d, hc.dpid = %d\n", grxsts.b.dpid, ++ hc->data_pid_start); ++ DWC_DEBUGPL(DBG_HCDV, " PStatus = %d\n", grxsts.b.pktsts); ++ ++ switch (grxsts.b.pktsts) { ++ case DWC_GRXSTS_PKTSTS_IN: ++ /* Read the data into the host buffer. */ ++ if (grxsts.b.bcnt > 0) { ++ dwc_otg_read_packet(dwc_otg_hcd->core_if, ++ hc->xfer_buff, grxsts.b.bcnt); ++ ++ /* Update the HC fields for the next packet received. */ ++ hc->xfer_count += grxsts.b.bcnt; ++ hc->xfer_buff += grxsts.b.bcnt; ++ } ++ ++ case DWC_GRXSTS_PKTSTS_IN_XFER_COMP: ++ case DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR: ++ case DWC_GRXSTS_PKTSTS_CH_HALTED: ++ /* Handled in interrupt, just ignore data */ ++ break; ++ default: ++ DWC_ERROR("RX_STS_Q Interrupt: Unknown status %d\n", ++ grxsts.b.pktsts); ++ break; ++ } ++ ++ return 1; ++} ++ ++/** This interrupt occurs when the non-periodic Tx FIFO is half-empty. More ++ * data packets may be written to the FIFO for OUT transfers. More requests ++ * may be written to the non-periodic request queue for IN transfers. This ++ * interrupt is enabled only in Slave mode. */ ++int32_t dwc_otg_hcd_handle_np_tx_fifo_empty_intr(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Non-Periodic TxFIFO Empty Interrupt--\n"); ++ dwc_otg_hcd_queue_transactions(dwc_otg_hcd, ++ DWC_OTG_TRANSACTION_NON_PERIODIC); ++ return 1; ++} ++ ++/** This interrupt occurs when the periodic Tx FIFO is half-empty. More data ++ * packets may be written to the FIFO for OUT transfers. More requests may be ++ * written to the periodic request queue for IN transfers. This interrupt is ++ * enabled only in Slave mode. */ ++int32_t dwc_otg_hcd_handle_perio_tx_fifo_empty_intr(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Periodic TxFIFO Empty Interrupt--\n"); ++ dwc_otg_hcd_queue_transactions(dwc_otg_hcd, ++ DWC_OTG_TRANSACTION_PERIODIC); ++ return 1; ++} ++ ++/** There are multiple conditions that can cause a port interrupt. This function ++ * determines which interrupt conditions have occurred and handles them ++ * appropriately. */ ++int32_t dwc_otg_hcd_handle_port_intr(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ int retval = 0; ++ hprt0_data_t hprt0; ++ hprt0_data_t hprt0_modify; ++ ++ hprt0.d32 = dwc_read_reg32(dwc_otg_hcd->core_if->host_if->hprt0); ++ hprt0_modify.d32 = dwc_read_reg32(dwc_otg_hcd->core_if->host_if->hprt0); ++ ++ /* Clear appropriate bits in HPRT0 to clear the interrupt bit in ++ * GINTSTS */ ++ ++ hprt0_modify.b.prtena = 0; ++ hprt0_modify.b.prtconndet = 0; ++ hprt0_modify.b.prtenchng = 0; ++ hprt0_modify.b.prtovrcurrchng = 0; ++ ++ /* Port Connect Detected ++ * Set flag and clear if detected */ ++ if (hprt0.b.prtconndet) { ++ DWC_DEBUGPL(DBG_HCD, "--Port Interrupt HPRT0=0x%08x " ++ "Port Connect Detected--\n", hprt0.d32); ++ dwc_otg_hcd->flags.b.port_connect_status_change = 1; ++ dwc_otg_hcd->flags.b.port_connect_status = 1; ++ hprt0_modify.b.prtconndet = 1; ++ ++ /* B-Device has connected, Delete the connection timer. */ ++ DWC_TIMER_CANCEL(dwc_otg_hcd->conn_timer); ++ ++ /* The Hub driver asserts a reset when it sees port connect ++ * status change flag */ ++ retval |= 1; ++ } ++ ++ /* Port Enable Changed ++ * Clear if detected - Set internal flag if disabled */ ++ if (hprt0.b.prtenchng) { ++ DWC_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x " ++ "Port Enable Changed--\n", hprt0.d32); ++ hprt0_modify.b.prtenchng = 1; ++ if (hprt0.b.prtena == 1) { ++ int do_reset = 0; ++ dwc_otg_core_params_t *params = ++ dwc_otg_hcd->core_if->core_params; ++ dwc_otg_core_global_regs_t *global_regs = ++ dwc_otg_hcd->core_if->core_global_regs; ++ dwc_otg_host_if_t *host_if = ++ dwc_otg_hcd->core_if->host_if; ++ ++ /* Check if we need to adjust the PHY clock speed for ++ * low power and adjust it */ ++ if (params->host_support_fs_ls_low_power) { ++ gusbcfg_data_t usbcfg; ++ ++ usbcfg.d32 = ++ dwc_read_reg32(&global_regs->gusbcfg); ++ ++ if (hprt0.b.prtspd == DWC_HPRT0_PRTSPD_LOW_SPEED ++ || hprt0.b.prtspd == ++ DWC_HPRT0_PRTSPD_FULL_SPEED) { ++ /* ++ * Low power ++ */ ++ hcfg_data_t hcfg; ++ if (usbcfg.b.phylpwrclksel == 0) { ++ /* Set PHY low power clock select for FS/LS devices */ ++ usbcfg.b.phylpwrclksel = 1; ++ dwc_write_reg32(&global_regs-> ++ gusbcfg, ++ usbcfg.d32); ++ do_reset = 1; ++ } ++ ++ hcfg.d32 = ++ dwc_read_reg32(&host_if-> ++ host_global_regs->hcfg); ++ ++ if (hprt0.b.prtspd == ++ DWC_HPRT0_PRTSPD_LOW_SPEED ++ && params-> ++ host_ls_low_power_phy_clk == ++ DWC_HOST_LS_LOW_POWER_PHY_CLK_PARAM_6MHZ) ++ { ++ /* 6 MHZ */ ++ DWC_DEBUGPL(DBG_CIL, ++ "FS_PHY programming HCFG to 6 MHz (Low Power)\n"); ++ if (hcfg.b.fslspclksel != ++ DWC_HCFG_6_MHZ) { ++ hcfg.b.fslspclksel = ++ DWC_HCFG_6_MHZ; ++ dwc_write_reg32 ++ (&host_if-> ++ host_global_regs-> ++ hcfg, hcfg.d32); ++ do_reset = 1; ++ } ++ } else { ++ /* 48 MHZ */ ++ DWC_DEBUGPL(DBG_CIL, ++ "FS_PHY programming HCFG to 48 MHz ()\n"); ++ if (hcfg.b.fslspclksel != ++ DWC_HCFG_48_MHZ) { ++ hcfg.b.fslspclksel = ++ DWC_HCFG_48_MHZ; ++ dwc_write_reg32 ++ (&host_if-> ++ host_global_regs-> ++ hcfg, hcfg.d32); ++ do_reset = 1; ++ } ++ } ++ } else { ++ /* ++ * Not low power ++ */ ++ if (usbcfg.b.phylpwrclksel == 1) { ++ usbcfg.b.phylpwrclksel = 0; ++ dwc_write_reg32(&global_regs-> ++ gusbcfg, ++ usbcfg.d32); ++ do_reset = 1; ++ } ++ } ++ ++ if (do_reset) { ++ DWC_TASK_SCHEDULE(dwc_otg_hcd-> ++ reset_tasklet); ++ } ++ } ++ ++ if (!do_reset) { ++ /* Port has been enabled set the reset change flag */ ++ dwc_otg_hcd->flags.b.port_reset_change = 1; ++ } ++ } else { ++ dwc_otg_hcd->flags.b.port_enable_change = 1; ++ } ++ retval |= 1; ++ } ++ ++ /** Overcurrent Change Interrupt */ ++ if (hprt0.b.prtovrcurrchng) { ++ DWC_DEBUGPL(DBG_HCD, " --Port Interrupt HPRT0=0x%08x " ++ "Port Overcurrent Changed--\n", hprt0.d32); ++ dwc_otg_hcd->flags.b.port_over_current_change = 1; ++ hprt0_modify.b.prtovrcurrchng = 1; ++ retval |= 1; ++ } ++ ++ /* Clear Port Interrupts */ ++ dwc_write_reg32(dwc_otg_hcd->core_if->host_if->hprt0, hprt0_modify.d32); ++ ++ return retval; ++} ++ ++/** This interrupt indicates that one or more host channels has a pending ++ * interrupt. There are multiple conditions that can cause each host channel ++ * interrupt. This function determines which conditions have occurred for each ++ * host channel interrupt and handles them appropriately. */ ++int32_t dwc_otg_hcd_handle_hc_intr(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ int i; ++ int retval = 0; ++ haint_data_t haint; ++ ++ /* Clear appropriate bits in HCINTn to clear the interrupt bit in ++ * GINTSTS */ ++ ++ haint.d32 = dwc_otg_read_host_all_channels_intr(dwc_otg_hcd->core_if); ++ ++ for (i = 0; i < dwc_otg_hcd->core_if->core_params->host_channels; i++) { ++ if (haint.b2.chint & (1 << i)) { ++ retval |= dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd, i); ++ } ++ } ++ ++ return retval; ++} ++ ++ ++ ++/** ++ * Gets the actual length of a transfer after the transfer halts. _halt_status ++ * holds the reason for the halt. ++ * ++ * For IN transfers where halt_status is DWC_OTG_HC_XFER_COMPLETE, ++ * *short_read is set to 1 upon return if less than the requested ++ * number of bytes were transferred. Otherwise, *short_read is set to 0 upon ++ * return. short_read may also be NULL on entry, in which case it remains ++ * unchanged. ++ */ ++static uint32_t get_actual_xfer_length(dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_halt_status_e halt_status, ++ int *short_read) ++{ ++ hctsiz_data_t hctsiz; ++ uint32_t length; ++ ++ if (short_read != NULL) { ++ *short_read = 0; ++ } ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ ++ if (halt_status == DWC_OTG_HC_XFER_COMPLETE) { ++ if (hc->ep_is_in) { ++ length = hc->xfer_len - hctsiz.b.xfersize; ++ if (short_read != NULL) { ++ *short_read = (hctsiz.b.xfersize != 0); ++ } ++ } else if (hc->qh->do_split) { ++ length = qtd->ssplit_out_xfer_count; ++ } else { ++ length = hc->xfer_len; ++ } ++ } else { ++ /* ++ * Must use the hctsiz.pktcnt field to determine how much data ++ * has been transferred. This field reflects the number of ++ * packets that have been transferred via the USB. This is ++ * always an integral number of packets if the transfer was ++ * halted before its normal completion. (Can't use the ++ * hctsiz.xfersize field because that reflects the number of ++ * bytes transferred via the AHB, not the USB). ++ */ ++ length = ++ (hc->start_pkt_count - hctsiz.b.pktcnt) * hc->max_packet; ++ } ++ ++ return length; ++} ++ ++/** ++ * Updates the state of the URB after a Transfer Complete interrupt on the ++ * host channel. Updates the actual_length field of the URB based on the ++ * number of bytes transferred via the host channel. Sets the URB status ++ * if the data transfer is finished. ++ * ++ * @return 1 if the data transfer specified by the URB is completely finished, ++ * 0 otherwise. ++ */ ++static int update_urb_state_xfer_comp(dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_hcd_urb_t * urb, ++ dwc_otg_qtd_t * qtd) ++{ ++ int xfer_done = 0; ++ int short_read = 0; ++ ++ int xfer_length; ++ ++ xfer_length = get_actual_xfer_length(hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_COMPLETE, ++ &short_read); ++ ++ ++ /* non DWORD-aligned buffer case handling. */ ++ if (hc->align_buff && xfer_length && hc->ep_is_in) { ++ dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf, xfer_length); ++ } ++ ++ urb->actual_length += xfer_length; ++ ++ if(xfer_length && (hc->ep_type == DWC_OTG_EP_TYPE_BULK) && ++ (urb->flags & URB_SEND_ZERO_PACKET) && (urb->actual_length == urb->length) && ++ !(urb->length % hc->max_packet)) { ++ xfer_done = 0; ++ } else if (short_read || urb->actual_length == urb->length) { ++ xfer_done = 1; ++ urb->status = 0; ++ } ++ ++#ifdef DEBUG ++ { ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n", ++ __func__, (hc->ep_is_in ? "IN" : "OUT"), ++ hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " hc->xfer_len %d\n", hc->xfer_len); ++ DWC_DEBUGPL(DBG_HCDV, " hctsiz.xfersize %d\n", ++ hctsiz.b.xfersize); ++ DWC_DEBUGPL(DBG_HCDV, " urb->transfer_buffer_length %d\n", ++ urb->length); ++ DWC_DEBUGPL(DBG_HCDV, " urb->actual_length %d\n", ++ urb->actual_length); ++ DWC_DEBUGPL(DBG_HCDV, " short_read %d, xfer_done %d\n", ++ short_read, xfer_done); ++ } ++#endif ++ ++ return xfer_done; ++} ++ ++/* ++ * Save the starting data toggle for the next transfer. The data toggle is ++ * saved in the QH for non-control transfers and it's saved in the QTD for ++ * control transfers. ++ */ ++void dwc_otg_hcd_save_data_toggle(dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, dwc_otg_qtd_t * qtd) ++{ ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ ++ if (hc->ep_type != DWC_OTG_EP_TYPE_CONTROL) { ++ dwc_otg_qh_t *qh = hc->qh; ++ if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) { ++ qh->data_toggle = DWC_OTG_HC_PID_DATA0; ++ } else { ++ qh->data_toggle = DWC_OTG_HC_PID_DATA1; ++ } ++ } else { ++ if (hctsiz.b.pid == DWC_HCTSIZ_DATA0) { ++ qtd->data_toggle = DWC_OTG_HC_PID_DATA0; ++ } else { ++ qtd->data_toggle = DWC_OTG_HC_PID_DATA1; ++ } ++ } ++} ++ ++/** ++ * Updates the state of an Isochronous URB when the transfer is stopped for ++ * any reason. The fields of the current entry in the frame descriptor array ++ * are set based on the transfer state and the input _halt_status. Completes ++ * the Isochronous URB if all the URB frames have been completed. ++ * ++ * @return DWC_OTG_HC_XFER_COMPLETE if there are more frames remaining to be ++ * transferred in the URB. Otherwise return DWC_OTG_HC_XFER_URB_COMPLETE. ++ */ ++static dwc_otg_halt_status_e ++update_isoc_urb_state(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd, dwc_otg_halt_status_e halt_status) ++{ ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ dwc_otg_halt_status_e ret_val = halt_status; ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc; ++ ++ frame_desc = &urb->iso_descs[qtd->isoc_frame_index]; ++ switch (halt_status) { ++ case DWC_OTG_HC_XFER_COMPLETE: ++ frame_desc->status = 0; ++ frame_desc->actual_length = ++ get_actual_xfer_length(hc, hc_regs, qtd, halt_status, NULL); ++ ++ /* non DWORD-aligned buffer case handling. */ ++ if (hc->align_buff && frame_desc->actual_length && hc->ep_is_in) { ++ dwc_memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset, ++ hc->qh->dw_align_buf, frame_desc->actual_length); ++ } ++ ++ break; ++ case DWC_OTG_HC_XFER_FRAME_OVERRUN: ++ urb->error_count++; ++ if (hc->ep_is_in) { ++ frame_desc->status = -DWC_E_NO_STREAM_RES; ++ } else { ++ frame_desc->status = -DWC_E_COMMUNICATION; ++ } ++ frame_desc->actual_length = 0; ++ break; ++ case DWC_OTG_HC_XFER_BABBLE_ERR: ++ urb->error_count++; ++ frame_desc->status = -DWC_E_OVERFLOW; ++ /* Don't need to update actual_length in this case. */ ++ break; ++ case DWC_OTG_HC_XFER_XACT_ERR: ++ urb->error_count++; ++ frame_desc->status = -DWC_E_PROTOCOL; ++ frame_desc->actual_length = ++ get_actual_xfer_length(hc, hc_regs, qtd, halt_status, NULL); ++ ++ /* non DWORD-aligned buffer case handling. */ ++ if (hc->align_buff && frame_desc->actual_length && hc->ep_is_in) { ++ dwc_memcpy(urb->buf + frame_desc->offset + qtd->isoc_split_offset, ++ hc->qh->dw_align_buf, frame_desc->actual_length); ++ } ++ /* Skip whole frame */ ++ if (hc->qh->do_split && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) && ++ hc->ep_is_in && hcd->core_if->dma_enable) { ++ qtd->complete_split = 0; ++ qtd->isoc_split_offset = 0; ++ } ++ ++ break; ++ default: ++ DWC_ASSERT(1, "Unhandled _halt_status (%d)\n", halt_status); ++ break; ++ } ++ if (++qtd->isoc_frame_index == urb->packet_count) { ++ /* ++ * urb->status is not used for isoc transfers. ++ * The individual frame_desc statuses are used instead. ++ */ ++ hcd->fops->complete(hcd, urb->priv, urb, 0); ++ ret_val = DWC_OTG_HC_XFER_URB_COMPLETE; ++ } else { ++ ret_val = DWC_OTG_HC_XFER_COMPLETE; ++ } ++ return ret_val; ++} ++ ++/** ++ * Frees the first QTD in the QH's list if free_qtd is 1. For non-periodic ++ * QHs, removes the QH from the active non-periodic schedule. If any QTDs are ++ * still linked to the QH, the QH is added to the end of the inactive ++ * non-periodic schedule. For periodic QHs, removes the QH from the periodic ++ * schedule if no more QTDs are linked to the QH. ++ */ ++static void deactivate_qh(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, int free_qtd) ++{ ++ int continue_split = 0; ++ dwc_otg_qtd_t *qtd; ++ ++ DWC_DEBUGPL(DBG_HCDV, " %s(%p,%p,%d)\n", __func__, hcd, qh, free_qtd); ++ ++ qtd = DWC_CIRCLEQ_FIRST(&qh->qtd_list); ++ ++ if (qtd->complete_split) { ++ continue_split = 1; ++ } else if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_MID || ++ qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_END) { ++ continue_split = 1; ++ } ++ ++ if (free_qtd) { ++ dwc_otg_hcd_qtd_remove_and_free(hcd, qtd, qh); ++ continue_split = 0; ++ } ++ ++ qh->channel = NULL; ++ dwc_otg_hcd_qh_deactivate(hcd, qh, continue_split); ++} ++ ++/** ++ * Releases a host channel for use by other transfers. Attempts to select and ++ * queue more transactions since at least one host channel is available. ++ * ++ * @param hcd The HCD state structure. ++ * @param hc The host channel to release. ++ * @param qtd The QTD associated with the host channel. This QTD may be freed ++ * if the transfer is complete or an error has occurred. ++ * @param halt_status Reason the channel is being released. This status ++ * determines the actions taken by this function. ++ */ ++static void release_channel(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ dwc_otg_transaction_type_e tr_type; ++ int free_qtd; ++ ++ DWC_DEBUGPL(DBG_HCDV, " %s: channel %d, halt_status %d, xfer_len %d\n", ++ __func__, hc->hc_num, halt_status, hc->xfer_len); ++ ++#ifdef HW2937_WORKAROUND ++ if (hcd->hw2937_assigned_channels & (1<<hc->hc_num)) ++ { ++ if ((hcd->hw2937_assigned_channels &= ~(1<<hc->hc_num)) == 0) ++ hcd->hw2937_xfer_mode = HW2937_XFER_MODE_IDLE; ++ DWC_DEBUGPL(DBG_HW2937, " release %d, hw2937_ac -> %x\n", hc->hc_num, hcd->hw2937_assigned_channels); ++ } ++ else ++ { ++ DWC_DEBUGPL(DBG_ANY, " Unexpected release %d (hw2937_ac = %x)\n", hc->hc_num, hcd->hw2937_assigned_channels); ++ } ++#endif ++ ++ switch (halt_status) { ++ case DWC_OTG_HC_XFER_URB_COMPLETE: ++ free_qtd = 1; ++ break; ++ case DWC_OTG_HC_XFER_AHB_ERR: ++ case DWC_OTG_HC_XFER_STALL: ++ case DWC_OTG_HC_XFER_BABBLE_ERR: ++ free_qtd = 1; ++ break; ++ case DWC_OTG_HC_XFER_XACT_ERR: ++ if (qtd->error_count >= 3) { ++ DWC_DEBUGPL(DBG_HCDV, ++ " Complete URB with transaction error\n"); ++ free_qtd = 1; ++ qtd->urb->status = -DWC_E_PROTOCOL; ++ hcd->fops->complete(hcd, qtd->urb->priv, ++ qtd->urb, -DWC_E_PROTOCOL); ++ } else { ++ free_qtd = 0; ++ } ++ break; ++ case DWC_OTG_HC_XFER_URB_DEQUEUE: ++ /* ++ * The QTD has already been removed and the QH has been ++ * deactivated. Don't want to do anything except release the ++ * host channel and try to queue more transfers. ++ */ ++ goto cleanup; ++ case DWC_OTG_HC_XFER_NO_HALT_STATUS: ++ free_qtd = 0; ++ break; ++ default: ++ free_qtd = 0; ++ break; ++ } ++ ++ deactivate_qh(hcd, hc->qh, free_qtd); ++ ++ cleanup: ++ /* ++ * Release the host channel for use by other transfers. The cleanup ++ * function clears the channel interrupt enables and conditions, so ++ * there's no need to clear the Channel Halted interrupt separately. ++ */ ++ dwc_otg_hc_cleanup(hcd->core_if, hc); ++ DWC_CIRCLEQ_INSERT_TAIL(&hcd->free_hc_list, hc, hc_list_entry); ++ ++ switch (hc->ep_type) { ++ case DWC_OTG_EP_TYPE_CONTROL: ++ case DWC_OTG_EP_TYPE_BULK: ++ hcd->non_periodic_channels--; ++ break; ++ ++ default: ++ /* ++ * Don't release reservations for periodic channels here. ++ * That's done when a periodic transfer is descheduled (i.e. ++ * when the QH is removed from the periodic schedule). ++ */ ++ break; ++ } ++ ++ /* Try to queue more transfers now that there's a free channel. */ ++ tr_type = dwc_otg_hcd_select_transactions(hcd); ++ if (tr_type != DWC_OTG_TRANSACTION_NONE) { ++ dwc_otg_hcd_queue_transactions(hcd, tr_type); ++ } ++} ++ ++ ++/** ++ * Halts a host channel. If the channel cannot be halted immediately because ++ * the request queue is full, this function ensures that the FIFO empty ++ * interrupt for the appropriate queue is enabled so that the halt request can ++ * be queued when there is space in the request queue. ++ * ++ * This function may also be called in DMA mode. In that case, the channel is ++ * simply released since the core always halts the channel automatically in ++ * DMA mode. ++ */ ++static void halt_channel(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_qtd_t * qtd, dwc_otg_halt_status_e halt_status) ++{ ++ if (hcd->core_if->dma_enable) { ++ release_channel(hcd, hc, qtd, halt_status); ++ return; ++ } ++ ++ /* Slave mode processing... */ ++ dwc_otg_hc_halt(hcd->core_if, hc, halt_status); ++ ++ if (hc->halt_on_queue) { ++ gintmsk_data_t gintmsk = {.d32 = 0 }; ++ dwc_otg_core_global_regs_t *global_regs; ++ global_regs = hcd->core_if->core_global_regs; ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL || ++ hc->ep_type == DWC_OTG_EP_TYPE_BULK) { ++ /* ++ * Make sure the Non-periodic Tx FIFO empty interrupt ++ * is enabled so that the non-periodic schedule will ++ * be processed. ++ */ ++ gintmsk.b.nptxfempty = 1; ++ dwc_modify_reg32(&global_regs->gintmsk, 0, gintmsk.d32); ++ } else { ++ /* ++ * Move the QH from the periodic queued schedule to ++ * the periodic assigned schedule. This allows the ++ * halt to be queued when the periodic schedule is ++ * processed. ++ */ ++ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_assigned, ++ &hc->qh->qh_list_entry); ++ ++ /* ++ * Make sure the Periodic Tx FIFO Empty interrupt is ++ * enabled so that the periodic schedule will be ++ * processed. ++ */ ++ gintmsk.b.ptxfempty = 1; ++ dwc_modify_reg32(&global_regs->gintmsk, 0, gintmsk.d32); ++ } ++ } ++} ++ ++/** ++ * Performs common cleanup for non-periodic transfers after a Transfer ++ * Complete interrupt. This function should be called after any endpoint type ++ * specific handling is finished to release the host channel. ++ */ ++static void complete_non_periodic_xfer(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ hcint_data_t hcint; ++ ++ qtd->error_count = 0; ++ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ if (hcint.b.nyet) { ++ /* ++ * Got a NYET on the last transaction of the transfer. This ++ * means that the endpoint should be in the PING state at the ++ * beginning of the next transfer. ++ */ ++ hc->qh->ping_state = 1; ++ clear_hc_int(hc_regs, nyet); ++ } ++ ++ /* ++ * Always halt and release the host channel to make it available for ++ * more transfers. There may still be more phases for a control ++ * transfer or more data packets for a bulk transfer at this point, ++ * but the host channel is still halted. A channel will be reassigned ++ * to the transfer when the non-periodic schedule is processed after ++ * the channel is released. This allows transactions to be queued ++ * properly via dwc_otg_hcd_queue_transactions, which also enables the ++ * Tx FIFO Empty interrupt if necessary. ++ */ ++ if (hc->ep_is_in) { ++ /* ++ * IN transfers in Slave mode require an explicit disable to ++ * halt the channel. (In DMA mode, this call simply releases ++ * the channel.) ++ */ ++ halt_channel(hcd, hc, qtd, halt_status); ++ } else { ++ /* ++ * The channel is automatically disabled by the core for OUT ++ * transfers in Slave mode. ++ */ ++ release_channel(hcd, hc, qtd, halt_status); ++ } ++} ++ ++/** ++ * Performs common cleanup for periodic transfers after a Transfer Complete ++ * interrupt. This function should be called after any endpoint type specific ++ * handling is finished to release the host channel. ++ */ ++static void complete_periodic_xfer(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ hctsiz_data_t hctsiz; ++ qtd->error_count = 0; ++ ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ if (!hc->ep_is_in || hctsiz.b.pktcnt == 0) { ++ /* Core halts channel in these cases. */ ++ release_channel(hcd, hc, qtd, halt_status); ++ } else { ++ /* Flush any outstanding requests from the Tx queue. */ ++ halt_channel(hcd, hc, qtd, halt_status); ++ } ++} ++ ++static int32_t handle_xfercomp_isoc_split_in(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ uint32_t len; ++ struct dwc_otg_hcd_iso_packet_desc *frame_desc; ++ frame_desc = &qtd->urb->iso_descs[qtd->isoc_frame_index]; ++ ++ len = get_actual_xfer_length(hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_COMPLETE, ++ NULL); ++ ++ if (!len) { ++ qtd->complete_split = 0; ++ qtd->isoc_split_offset = 0; ++ return 0; ++ } ++ frame_desc->actual_length += len; ++ ++ if (hc->align_buff && len) ++ dwc_memcpy(qtd->urb->buf + frame_desc->offset + qtd->isoc_split_offset, ++ hc->qh->dw_align_buf, ++ len); ++ qtd->isoc_split_offset += len; ++ ++ if (frame_desc->length == frame_desc->actual_length) { ++ frame_desc->status = 0; ++ qtd->isoc_frame_index++; ++ qtd->complete_split = 0; ++ qtd->isoc_split_offset = 0; ++ } ++ ++ if (qtd->isoc_frame_index == qtd->urb->packet_count) { ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } else { ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ } ++ ++ return 1; /* Indicates that channel released */ ++} ++/** ++ * Handles a host channel Transfer Complete interrupt. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_xfercomp_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ int urb_xfer_done; ++ dwc_otg_halt_status_e halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ int pipe_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info); ++ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Transfer Complete--\n", hc->hc_num); ++ ++ if (hcd->core_if->dma_desc_enable) { ++ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, halt_status); ++ if (pipe_type == UE_ISOCHRONOUS) { ++ /* Do not disable the interrupt, just clear it */ ++ clear_hc_int(hc_regs, xfercomp); ++ return 1; ++ } ++ goto handle_xfercomp_done; ++ } ++ ++ /* ++ * Handle xfer complete on CSPLIT. ++ */ ++ ++ if (hc->qh->do_split) { ++ if ((hc->ep_type == DWC_OTG_EP_TYPE_ISOC) && hc->ep_is_in && hcd->core_if->dma_enable) { ++ if (qtd->complete_split && handle_xfercomp_isoc_split_in(hcd, hc, hc_regs, qtd)) ++ goto handle_xfercomp_done; ++ } ++ else { ++ qtd->complete_split = 0; ++ } ++ } ++ ++ /* Update the QTD and URB states. */ ++ switch (pipe_type) { ++ case UE_CONTROL: ++ switch (qtd->control_phase) { ++ case DWC_OTG_CONTROL_SETUP: ++ if (urb->length > 0) { ++ qtd->control_phase = DWC_OTG_CONTROL_DATA; ++ } else { ++ qtd->control_phase = DWC_OTG_CONTROL_STATUS; ++ } ++ DWC_DEBUGPL(DBG_HCDV, ++ " Control setup transaction done\n"); ++ halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ break; ++ case DWC_OTG_CONTROL_DATA:{ ++ urb_xfer_done = ++ update_urb_state_xfer_comp(hc, hc_regs, urb, ++ qtd); ++ if (urb_xfer_done) { ++ qtd->control_phase = ++ DWC_OTG_CONTROL_STATUS; ++ DWC_DEBUGPL(DBG_HCDV, ++ " Control data transfer done\n"); ++ } else { ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ } ++ halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ break; ++ } ++ case DWC_OTG_CONTROL_STATUS: ++ DWC_DEBUGPL(DBG_HCDV, " Control transfer complete\n"); ++ if (urb->status == -DWC_E_IN_PROGRESS) { ++ urb->status = 0; ++ } ++ hcd->fops->complete(hcd, urb->priv, urb, urb->status); ++ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE; ++ break; ++ } ++ ++ complete_non_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status); ++ break; ++ case UE_BULK: ++ DWC_DEBUGPL(DBG_HCDV, " Bulk transfer complete\n"); ++ urb_xfer_done = ++ update_urb_state_xfer_comp(hc, hc_regs, urb, qtd); ++ if (urb_xfer_done) { ++ hcd->fops->complete(hcd, urb->priv, urb, urb->status); ++ halt_status = DWC_OTG_HC_XFER_URB_COMPLETE; ++ } else { ++ halt_status = DWC_OTG_HC_XFER_COMPLETE; ++ } ++ ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ complete_non_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status); ++ break; ++ case UE_INTERRUPT: ++ DWC_DEBUGPL(DBG_HCDV, " Interrupt transfer complete\n"); ++ update_urb_state_xfer_comp(hc, hc_regs, urb, qtd); ++ ++ /* ++ * Interrupt URB is done on the first transfer complete ++ * interrupt. ++ */ ++ hcd->fops->complete(hcd, urb->priv, urb, urb->status); ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ complete_periodic_xfer(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_URB_COMPLETE); ++ break; ++ case UE_ISOCHRONOUS: ++ DWC_DEBUGPL(DBG_HCDV, " Isochronous transfer complete\n"); ++ if (qtd->isoc_split_pos == DWC_HCSPLIT_XACTPOS_ALL) { ++ halt_status = ++ update_isoc_urb_state(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_COMPLETE); ++ } ++ complete_periodic_xfer(hcd, hc, hc_regs, qtd, halt_status); ++ break; ++ } ++ ++handle_xfercomp_done: ++ disable_hc_int(hc_regs, xfercompl); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel STALL interrupt. This handler may be called in ++ * either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_stall_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ int pipe_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info); ++ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "STALL Received--\n", hc->hc_num); ++ ++ if (hcd->core_if->dma_desc_enable) { ++ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, DWC_OTG_HC_XFER_STALL); ++ goto handle_stall_done; ++ } ++ ++ if (pipe_type == UE_CONTROL) { ++ hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_PIPE); ++ } ++ ++ if (pipe_type == UE_BULK || pipe_type == UE_INTERRUPT) { ++ hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_PIPE); ++ /* ++ * USB protocol requires resetting the data toggle for bulk ++ * and interrupt endpoints when a CLEAR_FEATURE(ENDPOINT_HALT) ++ * setup command is issued to the endpoint. Anticipate the ++ * CLEAR_FEATURE command since a STALL has occurred and reset ++ * the data toggle now. ++ */ ++ hc->qh->data_toggle = 0; ++ } ++ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_STALL); ++ ++handle_stall_done: ++ disable_hc_int(hc_regs, stall); ++ ++ return 1; ++} ++ ++/* ++ * Updates the state of the URB when a transfer has been stopped due to an ++ * abnormal condition before the transfer completes. Modifies the ++ * actual_length field of the URB to reflect the number of bytes that have ++ * actually been transferred via the host channel. ++ */ ++static void update_urb_state_xfer_intr(dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_hcd_urb_t * urb, ++ dwc_otg_qtd_t * qtd, ++ dwc_otg_halt_status_e halt_status) ++{ ++ uint32_t bytes_transferred = get_actual_xfer_length(hc, hc_regs, qtd, ++ halt_status, NULL); ++ /* non DWORD-aligned buffer case handling. */ ++ if (hc->align_buff && bytes_transferred && hc->ep_is_in) { ++ dwc_memcpy(urb->buf + urb->actual_length, hc->qh->dw_align_buf, bytes_transferred); ++ } ++ ++ urb->actual_length += bytes_transferred; ++ ++#ifdef DEBUG ++ { ++ hctsiz_data_t hctsiz; ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ DWC_DEBUGPL(DBG_HCDV, "DWC_otg: %s: %s, channel %d\n", ++ __func__, (hc->ep_is_in ? "IN" : "OUT"), ++ hc->hc_num); ++ DWC_DEBUGPL(DBG_HCDV, " hc->start_pkt_count %d\n", ++ hc->start_pkt_count); ++ DWC_DEBUGPL(DBG_HCDV, " hctsiz.pktcnt %d\n", hctsiz.b.pktcnt); ++ DWC_DEBUGPL(DBG_HCDV, " hc->max_packet %d\n", hc->max_packet); ++ DWC_DEBUGPL(DBG_HCDV, " bytes_transferred %d\n", ++ bytes_transferred); ++ DWC_DEBUGPL(DBG_HCDV, " urb->actual_length %d\n", ++ urb->actual_length); ++ DWC_DEBUGPL(DBG_HCDV, " urb->transfer_buffer_length %d\n", ++ urb->length); ++ } ++#endif ++} ++ ++/** ++ * Handles a host channel NAK interrupt. This handler may be called in either ++ * DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_nak_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "NAK Received--\n", hc->hc_num); ++ ++ /* ++ * Handle NAK for IN/OUT SSPLIT/CSPLIT transfers, bulk, control, and ++ * interrupt. Re-start the SSPLIT transfer. ++ */ ++ if (hc->do_split) { ++ if (hc->complete_split) { ++ qtd->error_count = 0; ++ } ++ qtd->complete_split = 0; ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK); ++ goto handle_nak_done; ++ } ++ ++ switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) { ++ case UE_CONTROL: ++ case UE_BULK: ++ if (hcd->core_if->dma_enable && hc->ep_is_in) { ++#ifdef HW2937_WORKAROUND ++ if (hc->halt_status == DWC_OTG_HC_XFER_PAUSE_IN) { ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK); ++ } ++#endif ++ /* ++ * NAK interrupts are enabled on bulk/control IN ++ * transfers in DMA mode for the sole purpose of ++ * resetting the error count after a transaction error ++ * occurs. The core will continue transferring data. ++ */ ++ qtd->error_count = 0; ++ goto handle_nak_done; ++ } ++ ++ /* ++ * NAK interrupts normally occur during OUT transfers in DMA ++ * or Slave mode. For IN transfers, more requests will be ++ * queued as request queue space is available. ++ */ ++ qtd->error_count = 0; ++ ++ if (!hc->qh->ping_state) { ++ update_urb_state_xfer_intr(hc, hc_regs, ++ qtd->urb, qtd, ++ DWC_OTG_HC_XFER_NAK); ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ ++ if (hc->speed == DWC_OTG_EP_SPEED_HIGH) ++ hc->qh->ping_state = 1; ++ } ++ ++ /* ++ * Halt the channel so the transfer can be re-started from ++ * the appropriate point or the PING protocol will ++ * start/continue. ++ */ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK); ++ break; ++ case UE_INTERRUPT: ++ qtd->error_count = 0; ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK); ++ break; ++ case UE_ISOCHRONOUS: ++#ifdef HW2937_WORKAROUND ++ if (hc->halt_status == DWC_OTG_HC_XFER_PAUSE_IN) { ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NAK); ++ break; ++ } ++#endif ++ /* Should never get called for isochronous transfers. */ ++ DWC_ASSERT(1, "NACK interrupt for ISOC transfer\n"); ++ break; ++ } ++ ++ handle_nak_done: ++ disable_hc_int(hc_regs, nak); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel ACK interrupt. This interrupt is enabled when ++ * performing the PING protocol in Slave mode, when errors occur during ++ * either Slave mode or DMA mode, and during Start Split transactions. ++ */ ++static int32_t handle_hc_ack_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "ACK Received--\n", hc->hc_num); ++ ++ if (hc->do_split) { ++ /* ++ * Handle ACK on SSPLIT. ++ * ACK should not occur in CSPLIT. ++ */ ++ if (!hc->ep_is_in && hc->data_pid_start != DWC_OTG_HC_PID_SETUP) { ++ qtd->ssplit_out_xfer_count = hc->xfer_len; ++ } ++ if (!(hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in)) { ++ /* Don't need complete for isochronous out transfers. */ ++ qtd->complete_split = 1; ++ } ++ ++ /* ISOC OUT */ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in) { ++ switch (hc->xact_pos) { ++ case DWC_HCSPLIT_XACTPOS_ALL: ++ break; ++ case DWC_HCSPLIT_XACTPOS_END: ++ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL; ++ qtd->isoc_split_offset = 0; ++ break; ++ case DWC_HCSPLIT_XACTPOS_BEGIN: ++ case DWC_HCSPLIT_XACTPOS_MID: ++ /* ++ * For BEGIN or MID, calculate the length for ++ * the next microframe to determine the correct ++ * SSPLIT token, either MID or END. ++ */ ++ { ++ struct dwc_otg_hcd_iso_packet_desc ++ *frame_desc; ++ ++ frame_desc = ++ &qtd->urb->iso_descs[qtd-> ++ isoc_frame_index]; ++ qtd->isoc_split_offset += 188; ++ ++ if ((frame_desc->length - ++ qtd->isoc_split_offset) <= 188) { ++ qtd->isoc_split_pos = ++ DWC_HCSPLIT_XACTPOS_END; ++ } else { ++ qtd->isoc_split_pos = ++ DWC_HCSPLIT_XACTPOS_MID; ++ } ++ ++ } ++ break; ++ } ++ } else { ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK); ++ } ++ } else { ++ qtd->error_count = 0; ++ ++ if (hc->qh->ping_state) { ++ hc->qh->ping_state = 0; ++ /* ++ * Halt the channel so the transfer can be re-started ++ * from the appropriate point. This only happens in ++ * Slave mode. In DMA mode, the ping_state is cleared ++ * when the transfer is started because the core ++ * automatically executes the PING, then the transfer. ++ */ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_ACK); ++ } ++#ifdef HW2937_WORKAROUND ++ else if (hc->halt_status == DWC_OTG_HC_XFER_PAUSE_IN) { ++ dwc_otg_hc_regs_t *hc_regs = hcd->core_if->host_if->hc_regs[hc->hc_num]; ++ update_urb_state_xfer_intr(hc, hc_regs, qtd->urb, qtd, DWC_OTG_HC_XFER_PAUSE_IN); ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_PAUSE_IN); ++ } ++#endif ++ } ++ ++ /* ++ * If the ACK occurred when _not_ in the PING state, let the channel ++ * continue transferring data after clearing the error count. ++ */ ++ ++ disable_hc_int(hc_regs, ack); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel NYET interrupt. This interrupt should only occur on ++ * Bulk and Control OUT endpoints and for complete split transactions. If a ++ * NYET occurs at the same time as a Transfer Complete interrupt, it is ++ * handled in the xfercomp interrupt handler, not here. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_nyet_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "NYET Received--\n", hc->hc_num); ++ ++ /* ++ * NYET on CSPLIT ++ * re-do the CSPLIT immediately on non-periodic ++ */ ++ if (hc->do_split && hc->complete_split) { ++ if (hc->ep_is_in && (hc->ep_type == DWC_OTG_EP_TYPE_ISOC) && hcd->core_if->dma_enable) { ++ qtd->complete_split = 0; ++ qtd->isoc_split_offset = 0; ++ if (++qtd->isoc_frame_index == qtd->urb->packet_count) { ++ hcd->fops->complete(hcd, qtd->urb->priv, qtd->urb, 0); ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_URB_COMPLETE); ++ } ++ else ++ release_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NO_HALT_STATUS); ++ goto handle_nyet_done; ++ } ++ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ int frnum = dwc_otg_hcd_get_frame_number(hcd); ++ ++ if (dwc_full_frame_num(frnum) != ++ dwc_full_frame_num(hc->qh->sched_frame)) { ++ /* ++ * No longer in the same full speed frame. ++ * Treat this as a transaction error. ++ */ ++#if 0 ++ /** @todo Fix system performance so this can ++ * be treated as an error. Right now complete ++ * splits cannot be scheduled precisely enough ++ * due to other system activity, so this error ++ * occurs regularly in Slave mode. ++ */ ++ qtd->error_count++; ++#endif ++ qtd->complete_split = 0; ++ halt_channel(hcd, hc, qtd, ++ DWC_OTG_HC_XFER_XACT_ERR); ++ /** @todo add support for isoc release */ ++ goto handle_nyet_done; ++ } ++ } ++ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET); ++ goto handle_nyet_done; ++ } ++ ++ hc->qh->ping_state = 1; ++ qtd->error_count = 0; ++ ++ update_urb_state_xfer_intr(hc, hc_regs, qtd->urb, qtd, ++ DWC_OTG_HC_XFER_NYET); ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ ++ /* ++ * Halt the channel and re-start the transfer so the PING ++ * protocol will start. ++ */ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_NYET); ++ ++ handle_nyet_done: ++ disable_hc_int(hc_regs, nyet); ++ return 1; ++} ++ ++/** ++ * Handles a host channel babble interrupt. This handler may be called in ++ * either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_babble_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Babble Error--\n", hc->hc_num); ++ ++ if (hcd->core_if->dma_desc_enable) { ++ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, DWC_OTG_HC_XFER_BABBLE_ERR); ++ goto handle_babble_done; ++ } ++ ++ if (hc->ep_type != DWC_OTG_EP_TYPE_ISOC) { ++ hcd->fops->complete(hcd, qtd->urb->priv, ++ qtd->urb, -DWC_E_OVERFLOW); ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_BABBLE_ERR); ++ } else { ++ dwc_otg_halt_status_e halt_status; ++ halt_status = update_isoc_urb_state(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_BABBLE_ERR); ++ halt_channel(hcd, hc, qtd, halt_status); ++ } ++ ++handle_babble_done: ++ disable_hc_int(hc_regs, bblerr); ++ return 1; ++} ++ ++/** ++ * Handles a host channel AHB error interrupt. This handler is only called in ++ * DMA mode. ++ */ ++static int32_t handle_hc_ahberr_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ hcchar_data_t hcchar; ++ hcsplt_data_t hcsplt; ++ hctsiz_data_t hctsiz; ++ uint32_t hcdma; ++ char *pipetype, *speed; ++ ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "AHB Error--\n", hc->hc_num); ++ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hcsplt.d32 = dwc_read_reg32(&hc_regs->hcsplt); ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ hcdma = dwc_read_reg32(&hc_regs->hcdma); ++ ++ DWC_ERROR("AHB ERROR, Channel %d\n", hc->hc_num); ++ DWC_ERROR(" hcchar 0x%08x, hcsplt 0x%08x\n", hcchar.d32, hcsplt.d32); ++ DWC_ERROR(" hctsiz 0x%08x, hcdma 0x%08x\n", hctsiz.d32, hcdma); ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Enqueue\n"); ++ DWC_ERROR(" Device address: %d\n", ++ dwc_otg_hcd_get_dev_addr(&urb->pipe_info)); ++ DWC_ERROR(" Endpoint: %d, %s\n", ++ dwc_otg_hcd_get_ep_num(&urb->pipe_info), ++ (dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT")); ++ ++ ++ switch (dwc_otg_hcd_get_pipe_type(&urb->pipe_info)) { ++case UE_CONTROL: ++ pipetype = "CONTROL"; ++ break; ++ case UE_BULK: ++ pipetype = "BULK"; ++ break; ++ case UE_INTERRUPT: ++ pipetype = "INTERRUPT"; ++ break; ++ case UE_ISOCHRONOUS: ++ pipetype = "ISOCHRONOUS"; ++ break; ++ default: ++ pipetype = "UNKNOWN"; ++ break; ++ } ++ ++ DWC_ERROR(" Endpoint type: %s\n", pipetype); ++ ++ switch (hc->speed) { ++ case DWC_OTG_EP_SPEED_HIGH: ++ speed = "HIGH"; ++ break; ++ case DWC_OTG_EP_SPEED_FULL: ++ speed = "FULL"; ++ break; ++ case DWC_OTG_EP_SPEED_LOW: ++ speed = "LOW"; ++ break; ++ default: ++ speed = "UNKNOWN"; ++ break; ++ }; ++ ++ DWC_ERROR(" Speed: %s\n", speed); ++ ++ DWC_ERROR(" Max packet size: %d\n", ++ dwc_otg_hcd_get_mps(&urb->pipe_info)); ++ DWC_ERROR(" Data buffer length: %d\n", urb->length); ++ DWC_ERROR(" Transfer buffer: %p, Transfer DMA: %p\n", ++ urb->buf, (void *)urb->dma); ++ DWC_ERROR(" Setup buffer: %p, Setup DMA: %p\n", ++ urb->setup_packet, (void *)urb->setup_dma); ++ DWC_ERROR(" Interval: %d\n", urb->interval); ++ ++ /* Core haltes the channel for Descriptor DMA mode */ ++ if (hcd->core_if->dma_desc_enable) { ++ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, DWC_OTG_HC_XFER_AHB_ERR); ++ goto handle_ahberr_done; ++ } ++ ++ hcd->fops->complete(hcd, urb->priv, urb, -DWC_E_IO); ++ ++ /* ++ * Force a channel halt. Don't call halt_channel because that won't ++ * write to the HCCHARn register in DMA mode to force the halt. ++ */ ++ dwc_otg_hc_halt(hcd->core_if, hc, DWC_OTG_HC_XFER_AHB_ERR); ++handle_ahberr_done: ++ disable_hc_int(hc_regs, ahberr); ++ return 1; ++} ++ ++/** ++ * Handles a host channel transaction error interrupt. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_xacterr_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Transaction Error--\n", hc->hc_num); ++ ++ if (hcd->core_if->dma_desc_enable) { ++ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, DWC_OTG_HC_XFER_XACT_ERR); ++ goto handle_xacterr_done; ++ } ++ ++ switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) { ++ case UE_CONTROL: ++ case UE_BULK: ++ qtd->error_count++; ++ if (!hc->qh->ping_state) { ++ ++ update_urb_state_xfer_intr(hc, hc_regs, ++ qtd->urb, qtd, ++ DWC_OTG_HC_XFER_XACT_ERR); ++ dwc_otg_hcd_save_data_toggle(hc, hc_regs, qtd); ++ if (!hc->ep_is_in && hc->speed == DWC_OTG_EP_SPEED_HIGH) { ++ hc->qh->ping_state = 1; ++ } ++ } ++ ++ /* ++ * Halt the channel so the transfer can be re-started from ++ * the appropriate point or the PING protocol will start. ++ */ ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ break; ++ case UE_INTERRUPT: ++ qtd->error_count++; ++ if (hc->do_split && hc->complete_split) { ++ qtd->complete_split = 0; ++ } ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_XACT_ERR); ++ break; ++ case UE_ISOCHRONOUS: ++ { ++ dwc_otg_halt_status_e halt_status; ++ halt_status = ++ update_isoc_urb_state(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_XACT_ERR); ++ ++ halt_channel(hcd, hc, qtd, halt_status); ++ } ++ break; ++ } ++handle_xacterr_done: ++ disable_hc_int(hc_regs, xacterr); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel frame overrun interrupt. This handler may be called ++ * in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_frmovrun_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Frame Overrun--\n", hc->hc_num); ++ ++ switch (dwc_otg_hcd_get_pipe_type(&qtd->urb->pipe_info)) { ++ case UE_CONTROL: ++ case UE_BULK: ++ break; ++ case UE_INTERRUPT: ++ halt_channel(hcd, hc, qtd, DWC_OTG_HC_XFER_FRAME_OVERRUN); ++ break; ++ case UE_ISOCHRONOUS: ++ { ++ dwc_otg_halt_status_e halt_status; ++ halt_status = ++ update_isoc_urb_state(hcd, hc, hc_regs, qtd, ++ DWC_OTG_HC_XFER_FRAME_OVERRUN); ++ ++ halt_channel(hcd, hc, qtd, halt_status); ++ } ++ break; ++ } ++ ++ disable_hc_int(hc_regs, frmovrun); ++ ++ return 1; ++} ++ ++/** ++ * Handles a host channel data toggle error interrupt. This handler may be ++ * called in either DMA mode or Slave mode. ++ */ ++static int32_t handle_hc_datatglerr_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Data Toggle Error--\n", hc->hc_num); ++ ++ if (hc->ep_is_in) { ++ qtd->error_count = 0; ++ } else { ++ DWC_ERROR("Data Toggle Error on OUT transfer," ++ "channel %d\n", hc->hc_num); ++ } ++ ++ disable_hc_int(hc_regs, datatglerr); ++ ++ return 1; ++} ++ ++#ifdef DEBUG ++/** ++ * This function is for debug only. It checks that a valid halt status is set ++ * and that HCCHARn.chdis is clear. If there's a problem, corrective action is ++ * taken and a warning is issued. ++ * @return 1 if halt status is ok, 0 otherwise. ++ */ ++static inline int halt_status_ok(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ hcchar_data_t hcchar; ++ hctsiz_data_t hctsiz; ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ hcsplt_data_t hcsplt; ++ ++ if (hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS) { ++ /* ++ * This code is here only as a check. This condition should ++ * never happen. Ignore the halt if it does occur. ++ */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ hctsiz.d32 = dwc_read_reg32(&hc_regs->hctsiz); ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ hcintmsk.d32 = dwc_read_reg32(&hc_regs->hcintmsk); ++ hcsplt.d32 = dwc_read_reg32(&hc_regs->hcsplt); ++ DWC_WARN ++ ("%s: hc->halt_status == DWC_OTG_HC_XFER_NO_HALT_STATUS, " ++ "channel %d, hcchar 0x%08x, hctsiz 0x%08x, " ++ "hcint 0x%08x, hcintmsk 0x%08x, " ++ "hcsplt 0x%08x, qtd->complete_split %d\n", __func__, ++ hc->hc_num, hcchar.d32, hctsiz.d32, hcint.d32, ++ hcintmsk.d32, hcsplt.d32, qtd->complete_split); ++ ++ DWC_WARN("%s: no halt status, channel %d, ignoring interrupt\n", ++ __func__, hc->hc_num); ++ DWC_WARN("\n"); ++ clear_hc_int(hc_regs, chhltd); ++ return 0; ++ } ++ ++ /* ++ * This code is here only as a check. hcchar.chdis should ++ * never be set when the halt interrupt occurs. Halt the ++ * channel again if it does occur. ++ */ ++ hcchar.d32 = dwc_read_reg32(&hc_regs->hcchar); ++ if (hcchar.b.chdis) { ++ DWC_WARN("%s: hcchar.chdis set unexpectedly, " ++ "hcchar 0x%08x, trying to halt again\n", ++ __func__, hcchar.d32); ++ clear_hc_int(hc_regs, chhltd); ++ hc->halt_pending = 0; ++ halt_channel(hcd, hc, qtd, hc->halt_status); ++ return 0; ++ } ++ ++ return 1; ++} ++#endif ++ ++/** ++ * Handles a host Channel Halted interrupt in DMA mode. This handler ++ * determines the reason the channel halted and proceeds accordingly. ++ */ ++static void handle_hc_chhltd_intr_dma(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ int out_nak_enh = 0; ++ ++ /* For core with OUT NAK enhancement, the flow for high- ++ * speed CONTROL/BULK OUT is handled a little differently. ++ */ ++ if (hcd->core_if->snpsid >= OTG_CORE_REV_2_71a) { ++ if (hc->speed == DWC_OTG_EP_SPEED_HIGH && !hc->ep_is_in && ++ (hc->ep_type == DWC_OTG_EP_TYPE_CONTROL || ++ hc->ep_type == DWC_OTG_EP_TYPE_BULK)) { ++ out_nak_enh = 1; ++ } ++ } ++ ++ if (hc->halt_status == DWC_OTG_HC_XFER_URB_DEQUEUE || ++ (hc->halt_status == DWC_OTG_HC_XFER_AHB_ERR && !hcd->core_if->dma_desc_enable)) { ++ /* ++ * Just release the channel. A dequeue can happen on a ++ * transfer timeout. In the case of an AHB Error, the channel ++ * was forced to halt because there's no way to gracefully ++ * recover. ++ */ ++ if (hcd->core_if->dma_desc_enable) ++ dwc_otg_hcd_complete_xfer_ddma(hcd, hc, hc_regs, hc->halt_status); ++ else ++ release_channel(hcd, hc, qtd, hc->halt_status); ++ return; ++ } ++ ++ /* Read the HCINTn register to determine the cause for the halt. */ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ hcintmsk.d32 = dwc_read_reg32(&hc_regs->hcintmsk); ++ ++ if (hcint.b.xfercomp) { ++ /** @todo This is here because of a possible hardware bug. Spec ++ * says that on SPLIT-ISOC OUT transfers in DMA mode that a HALT ++ * interrupt w/ACK bit set should occur, but I only see the ++ * XFERCOMP bit, even with it masked out. This is a workaround ++ * for that behavior. Should fix this when hardware is fixed. ++ */ ++ if (hc->ep_type == DWC_OTG_EP_TYPE_ISOC && !hc->ep_is_in) { ++ handle_hc_ack_intr(hcd, hc, hc_regs, qtd); ++ } ++ handle_hc_xfercomp_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.stall) { ++ handle_hc_stall_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.xacterr && !hcd->core_if->dma_desc_enable) { ++ if (out_nak_enh) { ++ if (hcint.b.nyet || hcint.b.nak || hcint.b.ack) { ++ DWC_DEBUG("XactErr with NYET/NAK/ACK\n"); ++ qtd->error_count = 0; ++ } else { ++ DWC_DEBUG("XactErr without NYET/NAK/ACK\n"); ++ } ++ } ++ ++ /* ++ * Must handle xacterr before nak or ack. Could get a xacterr ++ * at the same time as either of these on a BULK/CONTROL OUT ++ * that started with a PING. The xacterr takes precedence. ++ */ ++ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.xcs_xact && hcd->core_if->dma_desc_enable) { ++ handle_hc_xacterr_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.ahberr && hcd->core_if->dma_desc_enable) { ++ handle_hc_ahberr_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.bblerr) { ++ handle_hc_babble_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.frmovrun) { ++ handle_hc_frmovrun_intr(hcd, hc, hc_regs, qtd); ++ } else if (!out_nak_enh) { ++ if (hcint.b.nyet) { ++ /* ++ * Must handle nyet before nak or ack. Could get a nyet at the ++ * same time as either of those on a BULK/CONTROL OUT that ++ * started with a PING. The nyet takes precedence. ++ */ ++ handle_hc_nyet_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.nak && !hcintmsk.b.nak) { ++ /* ++ * If nak is not masked, it's because a non-split IN transfer ++ * is in an error state. In that case, the nak is handled by ++ * the nak interrupt handler, not here. Handle nak here for ++ * BULK/CONTROL OUT transfers, which halt on a NAK to allow ++ * rewinding the buffer pointer. ++ */ ++ handle_hc_nak_intr(hcd, hc, hc_regs, qtd); ++ } else if (hcint.b.ack && !hcintmsk.b.ack) { ++ /* ++ * If ack is not masked, it's because a non-split IN transfer ++ * is in an error state. In that case, the ack is handled by ++ * the ack interrupt handler, not here. Handle ack here for ++ * split transfers. Start splits halt on ACK. ++ */ ++ handle_hc_ack_intr(hcd, hc, hc_regs, qtd); ++ } else { ++ if (hc->ep_type == DWC_OTG_EP_TYPE_INTR || ++ hc->ep_type == DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * A periodic transfer halted with no other channel ++ * interrupts set. Assume it was halted by the core ++ * because it could not be completed in its scheduled ++ * (micro)frame. ++ */ ++#ifdef DEBUG ++ DWC_PRINTF ++ ("%s: Halt channel %d (assume incomplete periodic transfer)\n", ++ __func__, hc->hc_num); ++#endif ++ halt_channel(hcd, hc, qtd, ++ DWC_OTG_HC_XFER_PERIODIC_INCOMPLETE); ++ } else { ++ DWC_ERROR ++ ("%s: Channel %d, DMA Mode -- ChHltd set, but reason " ++ "for halting is unknown, hcint 0x%08x, intsts 0x%08x\n", ++ __func__, hc->hc_num, hcint.d32, ++ dwc_read_reg32(&hcd->core_if-> ++ core_global_regs->gintsts)); ++ } ++ ++ } ++ } else { ++ DWC_PRINTF("NYET/NAK/ACK/other in non-error case, 0x%08x\n", ++ hcint.d32); ++ } ++} ++ ++/** ++ * Handles a host channel Channel Halted interrupt. ++ * ++ * In slave mode, this handler is called only when the driver specifically ++ * requests a halt. This occurs during handling other host channel interrupts ++ * (e.g. nak, xacterr, stall, nyet, etc.). ++ * ++ * In DMA mode, this is the interrupt that occurs when the core has finished ++ * processing a transfer on a channel. Other host channel interrupts (except ++ * ahberr) are disabled in DMA mode. ++ */ ++static int32_t handle_hc_chhltd_intr(dwc_otg_hcd_t * hcd, ++ dwc_hc_t * hc, ++ dwc_otg_hc_regs_t * hc_regs, ++ dwc_otg_qtd_t * qtd) ++{ ++ DWC_DEBUGPL(DBG_HCD, "--Host Channel %d Interrupt: " ++ "Channel Halted--\n", hc->hc_num); ++ ++ if (hcd->core_if->dma_enable) { ++ handle_hc_chhltd_intr_dma(hcd, hc, hc_regs, qtd); ++ } else { ++#ifdef DEBUG ++ if (!halt_status_ok(hcd, hc, hc_regs, qtd)) { ++ return 1; ++ } ++#endif ++ release_channel(hcd, hc, qtd, hc->halt_status); ++ } ++ ++ return 1; ++} ++ ++/** Handles interrupt for a specific Host Channel */ ++int32_t dwc_otg_hcd_handle_hc_n_intr(dwc_otg_hcd_t * dwc_otg_hcd, uint32_t num) ++{ ++ int retval = 0; ++ hcint_data_t hcint; ++ hcintmsk_data_t hcintmsk; ++ dwc_hc_t *hc; ++ dwc_otg_hc_regs_t *hc_regs; ++ dwc_otg_qtd_t *qtd; ++ ++ DWC_DEBUGPL(DBG_HCDV, "--Host Channel Interrupt--, Channel %d\n", num); ++ ++ hc = dwc_otg_hcd->hc_ptr_array[num]; ++ hc_regs = dwc_otg_hcd->core_if->host_if->hc_regs[num]; ++ qtd = DWC_CIRCLEQ_FIRST(&hc->qh->qtd_list); ++ ++ hcint.d32 = dwc_read_reg32(&hc_regs->hcint); ++ hcintmsk.d32 = dwc_read_reg32(&hc_regs->hcintmsk); ++ DWC_DEBUGPL(DBG_HCDV, ++ " hcint 0x%08x, hcintmsk 0x%08x, hcint&hcintmsk 0x%08x\n", ++ hcint.d32, hcintmsk.d32, (hcint.d32 & hcintmsk.d32)); ++ hcint.d32 = hcint.d32 & hcintmsk.d32; ++ ++ if (!dwc_otg_hcd->core_if->dma_enable) { ++ if (hcint.b.chhltd && hcint.d32 != 0x2) { ++ hcint.b.chhltd = 0; ++ } ++ } ++ ++ if (hcint.b.xfercomp) { ++ retval |= ++ handle_hc_xfercomp_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ /* ++ * If NYET occurred at same time as Xfer Complete, the NYET is ++ * handled by the Xfer Complete interrupt handler. Don't want ++ * to call the NYET interrupt handler in this case. ++ */ ++ hcint.b.nyet = 0; ++ } ++ if (hcint.b.chhltd) { ++ retval |= handle_hc_chhltd_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.ahberr) { ++ retval |= handle_hc_ahberr_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.stall) { ++ retval |= handle_hc_stall_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.nak) { ++ retval |= handle_hc_nak_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.ack) { ++ retval |= handle_hc_ack_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.nyet) { ++ retval |= handle_hc_nyet_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.xacterr) { ++ retval |= handle_hc_xacterr_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.bblerr) { ++ retval |= handle_hc_babble_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.frmovrun) { ++ retval |= ++ handle_hc_frmovrun_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ if (hcint.b.datatglerr) { ++ retval |= ++ handle_hc_datatglerr_intr(dwc_otg_hcd, hc, hc_regs, qtd); ++ } ++ ++ return retval; ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c +@@ -0,0 +1,840 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_linux.c $ ++ * $Revision: #11 $ ++ * $Date: 2009/04/21 $ ++ * $Change: 1237476 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++/** ++ * @file ++ * ++ * This file contains the implementation of the HCD. In Linux, the HCD ++ * implements the hc_driver API. ++ */ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/errno.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/string.h> ++#include <linux/dma-mapping.h> ++#include <linux/version.h> ++#include <asm/io.h> ++ ++#ifdef LM_INTERFACE ++//#include <asm/arch/regs-irq.h> ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++#include <asm/arch/lm.h> ++#include <asm/arch/irqs.h> ++#else ++#include <mach/lm.h> ++#include <mach/irqs.h> ++#endif ++#elif defined(PLATFORM_INTERFACE) ++#include <linux/platform_device.h> ++#endif ++ ++#include <linux/usb.h> ++#include <linux/usb/hcd.h> ++ ++#include "dwc_otg_hcd_if.h" ++#include "dwc_otg_dbg.h" ++#include "dwc_otg_driver.h" ++ ++/** ++ * Gets the endpoint number from a _bEndpointAddress argument. The endpoint is ++ * qualified with its direction (possible 32 endpoints per device). ++ */ ++#define dwc_ep_addr_to_endpoint(_bEndpointAddress_) ((_bEndpointAddress_ & USB_ENDPOINT_NUMBER_MASK) | \ ++ ((_bEndpointAddress_ & USB_DIR_IN) != 0) << 4) ++ ++static const char dwc_otg_hcd_name[] = "dwc_otg_hcd"; ++ ++/** @name Linux HC Driver API Functions */ ++/** @{ */ ++/* manage i/o requests, device state */ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++static int urb_enqueue(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep, ++ struct urb *urb, gfp_t mem_flags); ++ ++static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb); ++#else ++static int urb_enqueue(struct usb_hcd *hcd, ++ struct urb *urb, gfp_t mem_flags); ++ ++static int urb_dequeue(struct usb_hcd *hcd, ++ struct urb *urb, int status); ++#endif ++ ++static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep); ++ ++static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd); ++extern int hcd_start(struct usb_hcd *hcd); ++extern void hcd_stop(struct usb_hcd *hcd); ++static int get_frame_number(struct usb_hcd *hcd); ++extern int hub_status_data(struct usb_hcd *hcd, char *buf); ++extern int hub_control(struct usb_hcd *hcd, ++ u16 typeReq, ++ u16 wValue, u16 wIndex, char *buf, u16 wLength); ++ ++struct wrapper_priv_data { ++ dwc_otg_hcd_t *dwc_otg_hcd; ++}; ++ ++/** @} */ ++ ++static struct hc_driver dwc_otg_hc_driver = { ++ ++ .description = dwc_otg_hcd_name, ++ .product_desc = "DWC OTG Controller", ++ .hcd_priv_size = sizeof(struct wrapper_priv_data), ++ ++ .irq = dwc_otg_hcd_irq, ++ ++ .flags = HCD_MEMORY | HCD_USB2, ++ ++ //.reset = ++ .start = hcd_start, ++ //.suspend = ++ //.resume = ++ .stop = hcd_stop, ++ ++ .urb_enqueue = urb_enqueue, ++ .urb_dequeue = urb_dequeue, ++ .endpoint_disable = endpoint_disable, ++ ++ .get_frame_number = get_frame_number, ++ ++ .hub_status_data = hub_status_data, ++ .hub_control = hub_control, ++ //.bus_suspend = ++ //.bus_resume = ++}; ++ ++/** Gets the dwc_otg_hcd from a struct usb_hcd */ ++static inline dwc_otg_hcd_t *hcd_to_dwc_otg_hcd(struct usb_hcd *hcd) ++{ ++ struct wrapper_priv_data *p; ++ p = (struct wrapper_priv_data *)(hcd->hcd_priv); ++ return p->dwc_otg_hcd; ++} ++ ++/** Gets the struct usb_hcd that contains a dwc_otg_hcd_t. */ ++static inline struct usb_hcd *dwc_otg_hcd_to_hcd(dwc_otg_hcd_t * dwc_otg_hcd) ++{ ++ return dwc_otg_hcd_get_priv_data(dwc_otg_hcd); ++} ++ ++/** Gets the usb_host_endpoint associated with an URB. */ ++inline struct usb_host_endpoint *dwc_urb_to_endpoint(struct urb *urb) ++{ ++ struct usb_device *dev = urb->dev; ++ int ep_num = usb_pipeendpoint(urb->pipe); ++ ++ if (usb_pipein(urb->pipe)) ++ return dev->ep_in[ep_num]; ++ else ++ return dev->ep_out[ep_num]; ++} ++ ++static int _disconnect(dwc_otg_hcd_t * hcd) ++{ ++ struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd); ++ ++ usb_hcd->self.is_b_host = 0; ++ return 0; ++} ++ ++static int _start(dwc_otg_hcd_t * hcd) ++{ ++ struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd); ++ ++ usb_hcd->self.is_b_host = dwc_otg_hcd_is_b_host(hcd); ++ hcd_start(usb_hcd); ++ ++ return 0; ++} ++ ++static int _hub_info(dwc_otg_hcd_t * hcd, void *urb_handle, uint32_t * hub_addr, ++ uint32_t * port_addr) ++{ ++ struct urb *urb = (struct urb *)urb_handle; ++#if 1 //GRAYG - temporary ++ if (NULL == urb_handle) ++ DWC_ERROR("**** %s - NULL URB handle\n", __func__);//GRAYG ++ if (NULL == urb->dev) ++ DWC_ERROR("**** %s - URB has no device\n", __func__);//GRAYG ++ if (NULL == port_addr) ++ DWC_ERROR("**** %s - NULL port_address\n", __func__);//GRAYG ++#endif ++ if (urb->dev->tt) { ++ if (NULL == urb->dev->tt->hub) { ++ DWC_ERROR("**** %s - (URB's transactor has no TT - giving no hub)\n", ++ __func__); //GRAYG ++ //*hub_addr = (u8)usb_pipedevice(urb->pipe); //GRAYG ++ *hub_addr = 0; //GRAYG ++ // we probably shouldn't have a transaction translator if ++ // there's no associated hub? ++ } else ++ *hub_addr = urb->dev->tt->hub->devnum; ++ } else { ++ *hub_addr = 0; ++ } ++ *port_addr = urb->dev->ttport; ++ return 0; ++} ++ ++static int _speed(dwc_otg_hcd_t * hcd, void *urb_handle) ++{ ++ struct urb *urb = (struct urb *)urb_handle; ++ return urb->dev->speed; ++} ++ ++static int _get_b_hnp_enable(dwc_otg_hcd_t * hcd) ++{ ++ struct usb_hcd *usb_hcd = dwc_otg_hcd_to_hcd(hcd); ++ return usb_hcd->self.b_hnp_enable; ++} ++ ++static void allocate_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw, ++ struct urb *urb) ++{ ++ hcd_to_bus(hcd)->bandwidth_allocated += bw / urb->interval; ++ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { ++ hcd_to_bus(hcd)->bandwidth_isoc_reqs++; ++ } else { ++ hcd_to_bus(hcd)->bandwidth_int_reqs++; ++ } ++} ++ ++static void free_bus_bandwidth(struct usb_hcd *hcd, uint32_t bw, ++ struct urb *urb) ++{ ++ hcd_to_bus(hcd)->bandwidth_allocated -= bw / urb->interval; ++ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { ++ hcd_to_bus(hcd)->bandwidth_isoc_reqs--; ++ } else { ++ hcd_to_bus(hcd)->bandwidth_int_reqs--; ++ } ++} ++ ++/** ++ * Sets the final status of an URB and returns it to the device driver. Any ++ * required cleanup of the URB is performed. ++ */ ++static int _complete(dwc_otg_hcd_t * hcd, void *urb_handle, ++ dwc_otg_hcd_urb_t * dwc_otg_urb, int32_t status) ++{ ++ struct urb *urb = (struct urb *)urb_handle; ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ DWC_PRINTF("%s: urb %p, device %d, ep %d %s, status=%d\n", ++ __func__, urb, usb_pipedevice(urb->pipe), ++ usb_pipeendpoint(urb->pipe), ++ usb_pipein(urb->pipe) ? "IN" : "OUT", status); ++ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { ++ int i; ++ for (i = 0; i < urb->number_of_packets; i++) { ++ DWC_PRINTF(" ISO Desc %d status: %d\n", ++ i, urb->iso_frame_desc[i].status); ++ } ++ } ++ } ++#endif ++ ++ urb->actual_length = dwc_otg_hcd_urb_get_actual_length(dwc_otg_urb); ++ /* Convert status value. */ ++ switch (status) { ++ case -DWC_E_PROTOCOL: ++ status = -EPROTO; ++ break; ++ case -DWC_E_IN_PROGRESS: ++ status = -EINPROGRESS; ++ break; ++ case -DWC_E_PIPE: ++ status = -EPIPE; ++ break; ++ case -DWC_E_IO: ++ status = -EIO; ++ break; ++ case -DWC_E_TIMEOUT: ++ status = -ETIMEDOUT; ++ break; ++ case -DWC_E_OVERFLOW: ++ status = -EOVERFLOW; ++ break; ++ default: ++ if (status) { ++ DWC_PRINTF("Uknown urb status %d\n", status); ++ ++ } ++ } ++ ++ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { ++ int i; ++ ++ urb->error_count = dwc_otg_hcd_urb_get_error_count(dwc_otg_urb); ++ for (i = 0; i < urb->number_of_packets; ++i) { ++ urb->iso_frame_desc[i].actual_length = ++ dwc_otg_hcd_urb_get_iso_desc_actual_length ++ (dwc_otg_urb, i); ++ urb->iso_frame_desc[i].status = ++ dwc_otg_hcd_urb_get_iso_desc_status ++ (dwc_otg_urb, i); ++ } ++ } ++ ++ urb->status = status; ++ urb->hcpriv = NULL; ++ if (!status) { ++ if ((urb->transfer_flags & URB_SHORT_NOT_OK) && ++ (urb->actual_length < urb->transfer_buffer_length)) { ++ urb->status = -EREMOTEIO; ++ } ++ } ++ ++ if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) || ++ (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) { ++ struct usb_host_endpoint *ep = dwc_urb_to_endpoint(urb); ++ if (ep) { ++ free_bus_bandwidth(dwc_otg_hcd_to_hcd(hcd), ++ dwc_otg_hcd_get_ep_bandwidth(hcd, ++ ep-> ++ hcpriv), ++ urb); ++ } ++ } ++ ++ dwc_free(dwc_otg_urb); ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb); ++#else ++ usb_hcd_unlink_urb_from_ep(dwc_otg_hcd_to_hcd(hcd), urb); ++ usb_hcd_giveback_urb(dwc_otg_hcd_to_hcd(hcd), urb, status); ++#endif ++ return 0; ++} ++ ++static struct dwc_otg_hcd_function_ops hcd_fops = { ++ .start = _start, ++ .disconnect = _disconnect, ++ .hub_info = _hub_info, ++ .speed = _speed, ++ .complete = _complete, ++ .get_b_hnp_enable = _get_b_hnp_enable, ++}; ++ ++/** ++ * Initializes the HCD. This function allocates memory for and initializes the ++ * static parts of the usb_hcd and dwc_otg_hcd structures. It also registers the ++ * USB bus with the core and calls the hc_driver->start() function. It returns ++ * a negative error on failure. ++ */ ++int hcd_init( ++#ifdef LM_INTERFACE ++ struct lm_device *_dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *_dev ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *_dev ++#endif ++ ) ++{ ++ struct usb_hcd *hcd = NULL; ++ dwc_otg_hcd_t *dwc_otg_hcd = NULL; ++#ifdef LM_INTERFACE ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); ++#endif ++ ++ int retval = 0; ++ u64 dmamask; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD INIT otg_dev=%p\n", otg_dev); ++ ++ /* Set device flags indicating whether the HCD supports DMA. */ ++ if (dwc_otg_is_dma_enable(otg_dev->core_if)) ++ dmamask = DMA_BIT_MASK(32); ++ else ++ dmamask = 0; ++ ++#if defined(LM_INTERFACE) || defined(PLATFORM_INTERFACE) ++ dma_set_mask(&_dev->dev, dmamask); ++ dma_set_coherent_mask(&_dev->dev, dmamask); ++#elif defined(PCI_INTERFACE) ++ pci_set_dma_mask(_dev, dmamask); ++ pci_set_consistent_dma_mask(_dev, dmamask); ++#endif ++ ++ /* ++ * Allocate memory for the base HCD plus the DWC OTG HCD. ++ * Initialize the base HCD. ++ */ ++ hcd = usb_create_hcd(&dwc_otg_hc_driver, &_dev->dev, ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++ _dev->dev.bus_id); ++#else ++ dev_name(&_dev->dev)); ++#endif ++ if (!hcd) { ++ retval = -ENOMEM; ++ goto error1; ++ } ++ ++ hcd->regs = otg_dev->base; ++ ++ /* Initialize the DWC OTG HCD. */ ++ dwc_otg_hcd = dwc_otg_hcd_alloc_hcd(); ++ if (!dwc_otg_hcd) { ++ goto error2; ++ } ++ ((struct wrapper_priv_data *)(hcd->hcd_priv))->dwc_otg_hcd = ++ dwc_otg_hcd; ++ otg_dev->hcd = dwc_otg_hcd; ++ ++ if (dwc_otg_hcd_init(dwc_otg_hcd, otg_dev->core_if)) { ++ goto error2; ++ } ++ ++ hcd->self.otg_port = dwc_otg_hcd_otg_port(dwc_otg_hcd); ++ ++ /* ++ * Finish generic HCD initialization and start the HCD. This function ++ * allocates the DMA buffer pool, registers the USB bus, requests the ++ * IRQ line, and calls hcd_start method. ++ */ ++#ifdef PLATFORM_INTERFACE ++ retval = usb_add_hcd(hcd, platform_get_irq(_dev, 0), IRQF_SHARED); ++#else ++ retval = usb_add_hcd(hcd, _dev->irq, IRQF_SHARED); ++#endif ++ if (retval < 0) { ++ goto error2; ++ } ++ ++ dwc_otg_hcd_set_priv_data(dwc_otg_hcd, hcd); ++ return 0; ++ ++ error2: ++ usb_put_hcd(hcd); ++ error1: ++ return retval; ++} ++ ++/** ++ * Removes the HCD. ++ * Frees memory and resources associated with the HCD and deregisters the bus. ++ */ ++void hcd_remove( ++#ifdef LM_INTERFACE ++ struct lm_device *_dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *_dev ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *_dev ++#endif ++ ) ++{ ++#ifdef LM_INTERFACE ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); ++#endif ++ ++ dwc_otg_hcd_t *dwc_otg_hcd; ++ struct usb_hcd *hcd; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD REMOVE otg_dev=%p\n", otg_dev); ++ ++ if (!otg_dev) { ++ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev NULL!\n", __func__); ++ return; ++ } ++ ++ dwc_otg_hcd = otg_dev->hcd; ++ ++ if (!dwc_otg_hcd) { ++ DWC_DEBUGPL(DBG_ANY, "%s: otg_dev->hcd NULL!\n", __func__); ++ return; ++ } ++ ++ hcd = dwc_otg_hcd_to_hcd(dwc_otg_hcd); ++ ++ if (!hcd) { ++ DWC_DEBUGPL(DBG_ANY, ++ "%s: dwc_otg_hcd_to_hcd(dwc_otg_hcd) NULL!\n", ++ __func__); ++ return; ++ } ++ usb_remove_hcd(hcd); ++ dwc_otg_hcd_set_priv_data(dwc_otg_hcd, NULL); ++ dwc_otg_hcd_remove(dwc_otg_hcd); ++ usb_put_hcd(hcd); ++} ++ ++/* ========================================================================= ++ * Linux HC Driver Functions ++ * ========================================================================= */ ++ ++/** Initializes the DWC_otg controller and its root hub and prepares it for host ++ * mode operation. Activates the root port. Returns 0 on success and a negative ++ * error code on failure. */ ++int hcd_start(struct usb_hcd *hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ struct usb_bus *bus; ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD START\n"); ++ bus = hcd_to_bus(hcd); ++ ++ hcd->state = HC_STATE_RUNNING; ++ if (dwc_otg_hcd_start(dwc_otg_hcd, &hcd_fops)) { ++ return 0; ++ } ++ ++ /* Initialize and connect root hub if one is not already attached */ ++ if (bus->root_hub) { ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD Has Root Hub\n"); ++ /* Inform the HUB driver to resume. */ ++ usb_hcd_resume_root_hub(hcd); ++ } ++ ++ return 0; ++} ++ ++/** ++ * Halts the DWC_otg host mode operations in a clean manner. USB transfers are ++ * stopped. ++ */ ++void hcd_stop(struct usb_hcd *hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ ++ dwc_otg_hcd_stop(dwc_otg_hcd); ++} ++ ++/** Returns the current frame number. */ ++static int get_frame_number(struct usb_hcd *hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ ++ return dwc_otg_hcd_get_frame_number(dwc_otg_hcd); ++} ++ ++#ifdef DEBUG ++static void dump_urb_info(struct urb *urb, char *fn_name) ++{ ++ DWC_PRINTF("%s, urb %p\n", fn_name, urb); ++ DWC_PRINTF(" Device address: %d\n", usb_pipedevice(urb->pipe)); ++ DWC_PRINTF(" Endpoint: %d, %s\n", usb_pipeendpoint(urb->pipe), ++ (usb_pipein(urb->pipe) ? "IN" : "OUT")); ++ DWC_PRINTF(" Endpoint type: %s\n", ( { ++ char *pipetype; ++ switch (usb_pipetype(urb->pipe)) { ++case PIPE_CONTROL: ++pipetype = "CONTROL"; break; case PIPE_BULK: ++pipetype = "BULK"; break; case PIPE_INTERRUPT: ++pipetype = "INTERRUPT"; break; case PIPE_ISOCHRONOUS: ++pipetype = "ISOCHRONOUS"; break; default: ++ pipetype = "UNKNOWN"; break;}; ++ pipetype;} ++ )) ; ++ DWC_PRINTF(" Speed: %s\n", ( { ++ char *speed; switch (urb->dev->speed) { ++case USB_SPEED_HIGH: ++speed = "HIGH"; break; case USB_SPEED_FULL: ++speed = "FULL"; break; case USB_SPEED_LOW: ++speed = "LOW"; break; default: ++ speed = "UNKNOWN"; break;}; ++ speed;} ++ )) ; ++ DWC_PRINTF(" Max packet size: %d\n", ++ usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))); ++ DWC_PRINTF(" Data buffer length: %d\n", urb->transfer_buffer_length); ++ DWC_PRINTF(" Transfer buffer: %p, Transfer DMA: %p\n", ++ urb->transfer_buffer, (void *)urb->transfer_dma); ++ DWC_PRINTF(" Setup buffer: %p, Setup DMA: %p\n", ++ urb->setup_packet, (void *)urb->setup_dma); ++ DWC_PRINTF(" Interval: %d\n", urb->interval); ++ if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) { ++ int i; ++ for (i = 0; i < urb->number_of_packets; i++) { ++ DWC_PRINTF(" ISO Desc %d:\n", i); ++ DWC_PRINTF(" offset: %d, length %d\n", ++ urb->iso_frame_desc[i].offset, ++ urb->iso_frame_desc[i].length); ++ } ++ } ++} ++ ++#endif ++ ++/** Starts processing a USB transfer request specified by a USB Request Block ++ * (URB). mem_flags indicates the type of memory allocation to use while ++ * processing this URB. */ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++static int urb_enqueue(struct usb_hcd *hcd, ++ struct usb_host_endpoint *ep, ++ struct urb *urb, gfp_t mem_flags) ++{ ++#else ++static int urb_enqueue(struct usb_hcd *hcd, ++ struct urb *urb, ++ gfp_t mem_flags) ++{ ++ struct usb_host_endpoint *ep = urb->ep; ++#endif ++ void **ref_ep_hcpriv = &ep->hcpriv; ++ int retval = 0; ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ dwc_otg_hcd_urb_t *dwc_otg_urb; ++ int i; ++ int alloc_bandwidth = 0; ++ uint8_t ep_type = 0; ++ uint32_t flags = 0; ++ void *buf; ++ ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ dump_urb_info(urb, "urb_enqueue"); ++ } ++#endif ++ ++ if (!urb->transfer_buffer && urb->transfer_buffer_length) ++ return -EINVAL; ++ ++ if ((usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) ++ || (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) { ++ if (!dwc_otg_hcd_is_bandwidth_allocated ++ (dwc_otg_hcd, ref_ep_hcpriv)) { ++ alloc_bandwidth = 1; ++ } ++ } ++ ++ switch (usb_pipetype(urb->pipe)) { ++ case PIPE_CONTROL: ++ ep_type = USB_ENDPOINT_XFER_CONTROL; ++ break; ++ case PIPE_ISOCHRONOUS: ++ ep_type = USB_ENDPOINT_XFER_ISOC; ++ break; ++ case PIPE_BULK: ++ ep_type = USB_ENDPOINT_XFER_BULK; ++ break; ++ case PIPE_INTERRUPT: ++ ep_type = USB_ENDPOINT_XFER_INT; ++ break; ++ default: ++ DWC_WARN("Wrong ep type\n"); ++ } ++ ++ dwc_otg_urb = dwc_otg_hcd_urb_alloc(dwc_otg_hcd, ++ urb->number_of_packets, ++ mem_flags == GFP_ATOMIC ? 1 : 0); ++ ++ urb->hcpriv = dwc_otg_urb; ++ ++ dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_urb, usb_pipedevice(urb->pipe), ++ usb_pipeendpoint(urb->pipe), ep_type, ++ usb_pipein(urb->pipe), ++ usb_maxpacket(urb->dev, urb->pipe, ++ !(usb_pipein(urb->pipe)))); ++ ++ buf = urb->transfer_buffer; ++ if (hcd->self.uses_dma) { ++ /* ++ * Calculate virtual address from physical address, ++ * because some class driver may not fill transfer_buffer. ++ * In Buffer DMA mode virual address is used, ++ * when handling non DWORD aligned buffers. ++ */ ++ //buf = phys_to_virt(urb->transfer_dma); ++ // DMA addresses are bus addresses not physical addresses! ++ buf = dma_to_virt(&urb->dev->dev, urb->transfer_dma); ++ } ++ ++ if (!(urb->transfer_flags & URB_NO_INTERRUPT)) ++ flags |= URB_GIVEBACK_ASAP; ++ if (urb->transfer_flags & URB_ZERO_PACKET) ++ flags |= URB_SEND_ZERO_PACKET; ++ ++ dwc_otg_hcd_urb_set_params(dwc_otg_urb, urb, buf, ++ urb->transfer_dma, ++ urb->transfer_buffer_length, ++ urb->setup_packet, ++ urb->setup_dma, ++ flags, ++ urb->interval); ++ ++ for (i = 0; i < urb->number_of_packets; ++i) { ++ dwc_otg_hcd_urb_set_iso_desc_params(dwc_otg_urb, i, ++ urb->iso_frame_desc[i]. ++ offset, ++ urb->iso_frame_desc[i]. ++ length); ++ } ++ ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)) ++ retval = usb_hcd_link_urb_to_ep(hcd, urb); ++ if (0 == retval) ++#endif ++ { ++ retval = dwc_otg_hcd_urb_enqueue(dwc_otg_hcd, dwc_otg_urb, ++ /*(dwc_otg_qh_t **)*/ ++ ref_ep_hcpriv); ++ if (0 == retval) { ++ if (alloc_bandwidth) { ++ allocate_bus_bandwidth(hcd, ++ dwc_otg_hcd_get_ep_bandwidth( ++ dwc_otg_hcd, *ref_ep_hcpriv), ++ urb); ++ } ++ } else { ++#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)) ++ usb_hcd_unlink_urb_from_ep(hcd, urb); ++#endif ++ if (retval == -DWC_E_NO_DEVICE) { ++ retval = -ENODEV; ++ } ++ } ++ } ++ return retval; ++} ++ ++/** Aborts/cancels a USB transfer request. Always returns 0 to indicate ++ * success. */ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb) ++#else ++static int urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) ++#endif ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd; ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD URB Dequeue\n"); ++ ++ dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ ++#ifdef DEBUG ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ dump_urb_info(urb, "urb_dequeue"); ++ } ++#endif ++ dwc_otg_hcd_urb_dequeue(dwc_otg_hcd, (dwc_otg_hcd_urb_t *)urb->hcpriv); ++ ++ dwc_free(urb->hcpriv); ++ urb->hcpriv = NULL; ++ ++ /* Higher layer software sets URB status. */ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++ usb_hcd_giveback_urb(hcd, urb); ++#else ++ usb_hcd_unlink_urb_from_ep(hcd, urb); ++ usb_hcd_giveback_urb(hcd, urb, status); ++#endif ++ if (CHK_DEBUG_LEVEL(DBG_HCDV | DBG_HCD_URB)) { ++ DWC_PRINTF("Called usb_hcd_giveback_urb()\n"); ++ DWC_PRINTF(" urb->status = %d\n", urb->status); ++ } ++ ++ return 0; ++} ++ ++/* Frees resources in the DWC_otg controller related to a given endpoint. Also ++ * clears state in the HCD related to the endpoint. Any URBs for the endpoint ++ * must already be dequeued. */ ++static void endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *ep) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ ++ DWC_DEBUGPL(DBG_HCD, ++ "DWC OTG HCD EP DISABLE: _bEndpointAddress=0x%02x, " ++ "endpoint=%d\n", ep->desc.bEndpointAddress, ++ dwc_ep_addr_to_endpoint(ep->desc.bEndpointAddress)); ++ dwc_otg_hcd_endpoint_disable(dwc_otg_hcd, ep->hcpriv, 250); ++ ep->hcpriv = NULL; ++} ++ ++/** Handles host mode interrupts for the DWC_otg controller. Returns IRQ_NONE if ++ * there was no interrupt to handle. Returns IRQ_HANDLED if there was a valid ++ * interrupt. ++ * ++ * This function is called by the USB core when an interrupt occurs */ ++static irqreturn_t dwc_otg_hcd_irq(struct usb_hcd *hcd) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ int32_t retval = dwc_otg_hcd_handle_intr(dwc_otg_hcd); ++ if (retval != 0) { ++ S3C2410X_CLEAR_EINTPEND(); ++ } ++ return IRQ_RETVAL(retval); ++} ++ ++/** Creates Status Change bitmap for the root hub and root port. The bitmap is ++ * returned in buf. Bit 0 is the status change indicator for the root hub. Bit 1 ++ * is the status change indicator for the single root port. Returns 1 if either ++ * change indicator is 1, otherwise returns 0. */ ++int hub_status_data(struct usb_hcd *hcd, char *buf) ++{ ++ dwc_otg_hcd_t *dwc_otg_hcd = hcd_to_dwc_otg_hcd(hcd); ++ ++ buf[0] = 0; ++ buf[0] |= (dwc_otg_hcd_is_status_changed(dwc_otg_hcd, 1)) << 1; ++ ++ return (buf[0] != 0); ++} ++ ++/** Handles hub class-specific requests. */ ++int hub_control(struct usb_hcd *hcd, ++ u16 typeReq, u16 wValue, u16 wIndex, char *buf, u16 wLength) ++{ ++ int retval; ++ ++ retval = dwc_otg_hcd_hub_control(hcd_to_dwc_otg_hcd(hcd), ++ typeReq, wValue, wIndex, buf, wLength); ++ ++ switch (retval) { ++ case -DWC_E_INVALID: ++ retval = -EINVAL; ++ break; ++ } ++ ++ return retval; ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_queue.c +@@ -0,0 +1,732 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_hcd_queue.c $ ++ * $Revision: #39 $ ++ * $Date: 2009/04/21 $ ++ * $Change: 1237477 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_DEVICE_ONLY ++ ++/** ++ * @file ++ * ++ * This file contains the functions to manage Queue Heads and Queue ++ * Transfer Descriptors. ++ */ ++ ++#include "dwc_otg_hcd.h" ++#include "dwc_otg_regs.h" ++ ++/** ++ * Free each QTD in the QH's QTD-list then free the QH. QH should already be ++ * removed from a list. QTD list should already be empty if called from URB ++ * Dequeue. ++ * ++ * @param hcd HCD instance. ++ * @param qh The QH to free. ++ */ ++void dwc_otg_hcd_qh_free(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ dwc_otg_qtd_t *qtd, *qtd_tmp; ++ uint64_t flags; ++ ++ /* Free each QTD in the QTD list */ ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ DWC_CIRCLEQ_FOREACH_SAFE(qtd, qtd_tmp, &qh->qtd_list, qtd_list_entry) { ++ DWC_CIRCLEQ_REMOVE(&qh->qtd_list, qtd, qtd_list_entry); ++ dwc_otg_hcd_qtd_free(qtd); ++ } ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ ++ if (hcd->core_if->dma_desc_enable) { ++ dwc_otg_hcd_qh_free_ddma(hcd, qh); ++ } ++ else if (qh->dw_align_buf) { ++ uint32_t buf_size; ++ if(qh->ep_type == UE_ISOCHRONOUS) { ++ buf_size = 4096; ++ } else { ++ buf_size = hcd->core_if->core_params->max_transfer_size; ++ } ++ dwc_dma_free(buf_size, qh->dw_align_buf, qh->dw_align_buf_dma); ++ } ++ ++ ++ ++ dwc_free(qh); ++ return; ++} ++ ++#define BitStuffTime(bytecount) ((8 * 7* bytecount) / 6) ++#define HS_HOST_DELAY 5 /* nanoseconds */ ++#define FS_LS_HOST_DELAY 1000 /* nanoseconds */ ++#define HUB_LS_SETUP 333 /* nanoseconds */ ++#define NS_TO_US(ns) ((ns + 500) / 1000) ++ /* convert & round nanoseconds to microseconds */ ++ ++static uint32_t calc_bus_time(int speed, int is_in, int is_isoc, ++ int bytecount) ++{ ++ unsigned long retval; ++ ++ switch (speed) { ++ case USB_SPEED_HIGH: ++ if (is_isoc) { ++ retval = ++ ((38 * 8 * 2083) + ++ (2083 * (3 + BitStuffTime(bytecount)))) / 1000 + ++ HS_HOST_DELAY; ++ } else { ++ retval = ++ ((55 * 8 * 2083) + ++ (2083 * (3 + BitStuffTime(bytecount)))) / 1000 + ++ HS_HOST_DELAY; ++ } ++ break; ++ case USB_SPEED_FULL: ++ if (is_isoc) { ++ retval = ++ (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000; ++ if (is_in) { ++ retval = 7268 + FS_LS_HOST_DELAY + retval; ++ } else { ++ retval = 6265 + FS_LS_HOST_DELAY + retval; ++ } ++ } else { ++ retval = ++ (8354 * (31 + 10 * BitStuffTime(bytecount))) / 1000; ++ retval = 9107 + FS_LS_HOST_DELAY + retval; ++ } ++ break; ++ case USB_SPEED_LOW: ++ if (is_in) { ++ retval = ++ (67667 * (31 + 10 * BitStuffTime(bytecount))) / ++ 1000; ++ retval = ++ 64060 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY + ++ retval; ++ } else { ++ retval = ++ (66700 * (31 + 10 * BitStuffTime(bytecount))) / ++ 1000; ++ retval = ++ 64107 + (2 * HUB_LS_SETUP) + FS_LS_HOST_DELAY + ++ retval; ++ } ++ break; ++ default: ++ DWC_WARN("Unknown device speed\n"); ++ retval = -1; ++ } ++ ++ return NS_TO_US(retval); ++} ++ ++/** ++ * Initializes a QH structure. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh The QH to init. ++ * @param urb Holds the information about the device/endpoint that we need ++ * to initialize the QH. ++ */ ++#define SCHEDULE_SLOP 10 ++void qh_init(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, ++ dwc_otg_hcd_urb_t * urb) ++{ ++ char *speed, *type; ++ int dev_speed; ++ uint32_t hub_addr, hub_port; ++ ++ dwc_memset(qh, 0, sizeof(dwc_otg_qh_t)); ++ ++ /* Initialize QH */ ++ qh->ep_type = dwc_otg_hcd_get_pipe_type(&urb->pipe_info); ++ ++ qh->ep_is_in = dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? 1 : 0; ++ ++ qh->data_toggle = DWC_OTG_HC_PID_DATA0; ++ qh->maxp = dwc_otg_hcd_get_mps(&urb->pipe_info); ++ DWC_CIRCLEQ_INIT(&qh->qtd_list); ++ DWC_LIST_INIT(&qh->qh_list_entry); ++ qh->channel = NULL; ++ ++ /* FS/LS Enpoint on HS Hub ++ * NOT virtual root hub */ ++ dev_speed = hcd->fops->speed(hcd, urb->priv); ++ hcd->fops->hub_info(hcd, urb->priv, &hub_addr, &hub_port); ++ qh->do_split = 0; ++ if (((dev_speed == USB_SPEED_LOW) || ++ (dev_speed == USB_SPEED_FULL)) && ++ (hub_addr != 0 && hub_addr != 1)) { ++ ++ DWC_DEBUGPL(DBG_HCD, ++ "QH init: EP %d: TT found at hub addr %d, for port %d\n", ++ dwc_otg_hcd_get_ep_num(&urb->pipe_info), hub_addr, ++ hub_port); ++ ++ qh->do_split = 1; ++ } ++ ++ if (qh->ep_type == UE_INTERRUPT || qh->ep_type == UE_ISOCHRONOUS) { ++ /* Compute scheduling parameters once and save them. */ ++ hprt0_data_t hprt; ++ ++ /** @todo Account for split transfers in the bus time. */ ++ int bytecount = ++ dwc_hb_mult(qh->maxp) * dwc_max_packet(qh->maxp); ++ ++ qh->usecs = calc_bus_time((qh->do_split ? USB_SPEED_HIGH : dev_speed), ++ qh->ep_is_in, ++ (qh->ep_type == UE_ISOCHRONOUS), ++ bytecount); ++ /* Start in a slightly future (micro)frame. */ ++ qh->sched_frame = dwc_frame_num_inc(hcd->frame_number, ++ SCHEDULE_SLOP); ++ qh->interval = urb->interval; ++ ++#if 0 ++ /* Increase interrupt polling rate for debugging. */ ++ if (qh->ep_type == UE_INTERRUPT) { ++ qh->interval = 8; ++ } ++#endif ++ hprt.d32 = dwc_read_reg32(hcd->core_if->host_if->hprt0); ++ if ((hprt.b.prtspd == DWC_HPRT0_PRTSPD_HIGH_SPEED) && ++ ((dev_speed == USB_SPEED_LOW) || ++ (dev_speed == USB_SPEED_FULL))) { ++ qh->interval *= 8; ++ qh->sched_frame |= 0x7; ++ qh->start_split_frame = qh->sched_frame; ++ } ++ ++ } ++ ++ DWC_DEBUGPL(DBG_HCD, "DWC OTG HCD QH Initialized\n"); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - qh = %p\n", qh); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Device Address = %d\n", ++ dwc_otg_hcd_get_dev_addr(&urb->pipe_info)); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Endpoint %d, %s\n", ++ dwc_otg_hcd_get_ep_num(&urb->pipe_info), ++ dwc_otg_hcd_is_pipe_in(&urb->pipe_info) ? "IN" : "OUT"); ++ switch (dev_speed) { ++ case USB_SPEED_LOW: ++ qh->dev_speed = DWC_OTG_EP_SPEED_LOW; ++ speed = "low"; ++ break; ++ case USB_SPEED_FULL: ++ qh->dev_speed = DWC_OTG_EP_SPEED_FULL; ++ speed = "full"; ++ break; ++ case USB_SPEED_HIGH: ++ qh->dev_speed = DWC_OTG_EP_SPEED_HIGH; ++ speed = "high"; ++ break; ++ default: ++ speed = "?"; ++ break; ++ } ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Speed = %s\n", speed); ++ ++ switch (qh->ep_type) { ++ case UE_ISOCHRONOUS: ++ type = "isochronous"; ++ break; ++ case UE_INTERRUPT: ++ type = "interrupt"; ++ break; ++ case UE_CONTROL: ++ type = "control"; ++ break; ++ case UE_BULK: ++ type = "bulk"; ++ break; ++ default: ++ type = "?"; ++ break; ++ } ++ ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - Type = %s\n", type); ++ ++#ifdef DEBUG ++ if (qh->ep_type == UE_INTERRUPT) { ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - usecs = %d\n", ++ qh->usecs); ++ DWC_DEBUGPL(DBG_HCDV, "DWC OTG HCD QH - interval = %d\n", ++ qh->interval); ++ } ++#endif ++ ++} ++ ++/** ++ * This function allocates and initializes a QH. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param urb Holds the information about the device/endpoint that we need ++ * to initialize the QH. ++ * ++ * @return Returns pointer to the newly allocated QH, or NULL on error. */ ++dwc_otg_qh_t *dwc_otg_hcd_qh_create(dwc_otg_hcd_t * hcd, ++ dwc_otg_hcd_urb_t * urb) ++{ ++ dwc_otg_qh_t *qh; ++ ++ /* Allocate memory */ ++ /** @todo add memflags argument */ ++ qh = dwc_otg_hcd_qh_alloc(); ++ if (qh == NULL) { ++ return NULL; ++ } ++ ++ qh_init(hcd, qh, urb); ++ ++ if (hcd->core_if->dma_desc_enable && (dwc_otg_hcd_qh_init_ddma(hcd, qh) < 0)) { ++ dwc_otg_hcd_qh_free(hcd, qh); ++ return NULL; ++ } ++ ++ return qh; ++} ++ ++/** ++ * Checks that a channel is available for a periodic transfer. ++ * ++ * @return 0 if successful, negative error code otherise. ++ */ ++static int periodic_channel_available(dwc_otg_hcd_t * hcd) ++{ ++ /* ++ * Currently assuming that there is a dedicated host channnel for each ++ * periodic transaction plus at least one host channel for ++ * non-periodic transactions. ++ */ ++ int status; ++ int num_channels; ++ ++ num_channels = hcd->core_if->core_params->host_channels; ++ if ((hcd->periodic_channels + hcd->non_periodic_channels < num_channels) && ++ (hcd->periodic_channels < num_channels - 1)) { ++ status = 0; ++ } else { ++ DWC_INFO("%s: Total channels: %d, Periodic: %d, Non-periodic: %d\n", ++ __func__, num_channels, hcd->periodic_channels, hcd->non_periodic_channels); //NOTICE ++ status = -DWC_E_NO_SPACE; ++ } ++ ++ return status; ++} ++ ++/** ++ * Checks that there is sufficient bandwidth for the specified QH in the ++ * periodic schedule. For simplicity, this calculation assumes that all the ++ * transfers in the periodic schedule may occur in the same (micro)frame. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh QH containing periodic bandwidth required. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++static int check_periodic_bandwidth(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ int status; ++ int16_t max_claimed_usecs; ++ ++ status = 0; ++ ++ if ((qh->dev_speed == DWC_OTG_EP_SPEED_HIGH) || qh->do_split) { ++ /* ++ * High speed mode. ++ * Max periodic usecs is 80% x 125 usec = 100 usec. ++ */ ++ ++ max_claimed_usecs = 100 - qh->usecs; ++ } else { ++ /* ++ * Full speed mode. ++ * Max periodic usecs is 90% x 1000 usec = 900 usec. ++ */ ++ max_claimed_usecs = 900 - qh->usecs; ++ } ++ ++ if (hcd->periodic_usecs > max_claimed_usecs) { ++ DWC_INFO("%s: already claimed usecs %d, required usecs %d\n", __func__, hcd->periodic_usecs, qh->usecs); //NOTICE ++ status = -DWC_E_NO_SPACE; ++ } ++ ++ return status; ++} ++ ++/** ++ * Checks that the max transfer size allowed in a host channel is large enough ++ * to handle the maximum data transfer in a single (micro)frame for a periodic ++ * transfer. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh QH for a periodic endpoint. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++static int check_max_xfer_size(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ int status; ++ uint32_t max_xfer_size; ++ uint32_t max_channel_xfer_size; ++ ++ status = 0; ++ ++ max_xfer_size = dwc_max_packet(qh->maxp) * dwc_hb_mult(qh->maxp); ++ max_channel_xfer_size = hcd->core_if->core_params->max_transfer_size; ++ ++ if (max_xfer_size > max_channel_xfer_size) { ++ DWC_INFO("%s: Periodic xfer length %d > " "max xfer length for channel %d\n", ++ __func__, max_xfer_size, max_channel_xfer_size); //NOTICE ++ status = -DWC_E_NO_SPACE; ++ } ++ ++ return status; ++} ++ ++/** ++ * Schedules an interrupt or isochronous transfer in the periodic schedule. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh QH for the periodic transfer. The QH should already contain the ++ * scheduling information. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++static int schedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ int status = 0; ++ ++ status = periodic_channel_available(hcd); ++ if (status) { ++ DWC_INFO("%s: No host channel available for periodic " "transfer.\n", __func__); //NOTICE ++ return status; ++ } ++ ++ status = check_periodic_bandwidth(hcd, qh); ++ if (status) { ++ DWC_INFO("%s: Insufficient periodic bandwidth for " "periodic transfer.\n", __func__); //NOTICE ++ return status; ++ } ++ ++ status = check_max_xfer_size(hcd, qh); ++ if (status) { ++ DWC_INFO("%s: Channel max transfer size too small " "for periodic transfer.\n", __func__); //NOTICE ++ return status; ++ } ++ ++ if (hcd->core_if->dma_desc_enable) { ++ /* Don't rely on SOF and start in ready schedule */ ++ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_ready, &qh->qh_list_entry); ++ } ++ else { ++ /* Always start in the inactive schedule. */ ++ DWC_LIST_INSERT_TAIL(&hcd->periodic_sched_inactive, &qh->qh_list_entry); ++ } ++ ++ /* Reserve the periodic channel. */ ++ hcd->periodic_channels++; ++ ++ /* Update claimed usecs per (micro)frame. */ ++ hcd->periodic_usecs += qh->usecs; ++ ++ return status; ++} ++ ++/** ++ * This function adds a QH to either the non periodic or periodic schedule if ++ * it is not already in the schedule. If the QH is already in the schedule, no ++ * action is taken. ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++int dwc_otg_hcd_qh_add(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ int status = 0; ++ uint64_t flags; ++ ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ ++ if (!DWC_LIST_EMPTY(&qh->qh_list_entry)) { ++ /* QH already in a schedule. */ ++ goto done; ++ } ++ ++ /* Add the new QH to the appropriate schedule */ ++ if (dwc_qh_is_non_per(qh)) { ++ /* Always start in the inactive schedule. */ ++ DWC_LIST_INSERT_TAIL(&hcd->non_periodic_sched_inactive, ++ &qh->qh_list_entry); ++ } else { ++ status = schedule_periodic(hcd, qh); ++ } ++ ++ done: ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ ++ return status; ++} ++ ++/** ++ * Removes an interrupt or isochronous transfer from the periodic schedule. ++ * ++ * @param hcd The HCD state structure for the DWC OTG controller. ++ * @param qh QH for the periodic transfer. ++ */ ++static void deschedule_periodic(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ DWC_LIST_REMOVE_INIT(&qh->qh_list_entry); ++ ++ /* Release the periodic channel reservation. */ ++ hcd->periodic_channels--; ++ ++ /* Update claimed usecs per (micro)frame. */ ++ hcd->periodic_usecs -= qh->usecs; ++} ++ ++/** ++ * Removes a QH from either the non-periodic or periodic schedule. Memory is ++ * not freed. ++ * ++ * @param hcd The HCD state structure. ++ * @param qh QH to remove from schedule. */ ++void dwc_otg_hcd_qh_remove(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh) ++{ ++ uint64_t flags; ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ ++ if (DWC_LIST_EMPTY(&qh->qh_list_entry)) { ++ /* QH is not in a schedule. */ ++ goto done; ++ } ++ ++ if (dwc_qh_is_non_per(qh)) { ++ if (hcd->non_periodic_qh_ptr == &qh->qh_list_entry) { ++ hcd->non_periodic_qh_ptr = ++ hcd->non_periodic_qh_ptr->next; ++ } ++ DWC_LIST_REMOVE_INIT(&qh->qh_list_entry); ++ } else { ++ deschedule_periodic(hcd, qh); ++ } ++ ++ done: ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++} ++ ++/** ++ * Deactivates a QH. For non-periodic QHs, removes the QH from the active ++ * non-periodic schedule. The QH is added to the inactive non-periodic ++ * schedule if any QTDs are still attached to the QH. ++ * ++ * For periodic QHs, the QH is removed from the periodic queued schedule. If ++ * there are any QTDs still attached to the QH, the QH is added to either the ++ * periodic inactive schedule or the periodic ready schedule and its next ++ * scheduled frame is calculated. The QH is placed in the ready schedule if ++ * the scheduled frame has been reached already. Otherwise it's placed in the ++ * inactive schedule. If there are no QTDs attached to the QH, the QH is ++ * completely removed from the periodic schedule. ++ */ ++void dwc_otg_hcd_qh_deactivate(dwc_otg_hcd_t * hcd, dwc_otg_qh_t * qh, ++ int sched_next_periodic_split) ++{ ++ uint64_t flags; ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ ++ if (dwc_qh_is_non_per(qh)) { ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ if (!DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { ++ /* Add back to inactive non-periodic schedule. */ ++ dwc_otg_hcd_qh_add(hcd, qh); ++ } ++ } else { ++ uint16_t frame_number = dwc_otg_hcd_get_frame_number(hcd); ++ ++ if (qh->do_split) { ++ /* Schedule the next continuing periodic split transfer */ ++ if (sched_next_periodic_split) { ++ ++ qh->sched_frame = frame_number; ++ if (dwc_frame_num_le(frame_number, ++ dwc_frame_num_inc(qh-> ++ start_split_frame, ++ 1))) { ++ /* ++ * Allow one frame to elapse after start ++ * split microframe before scheduling ++ * complete split, but DONT if we are ++ * doing the next start split in the ++ * same frame for an ISOC out. ++ */ ++ if ((qh->ep_type != UE_ISOCHRONOUS) || ++ (qh->ep_is_in != 0)) { ++ qh->sched_frame = ++ dwc_frame_num_inc(qh->sched_frame, 1); ++ } ++ } ++ } else { ++ qh->sched_frame = ++ dwc_frame_num_inc(qh->start_split_frame, ++ qh->interval); ++ if (dwc_frame_num_le ++ (qh->sched_frame, frame_number)) { ++ qh->sched_frame = frame_number; ++ } ++ qh->sched_frame |= 0x7; ++ qh->start_split_frame = qh->sched_frame; ++ } ++ } else { ++ qh->sched_frame = ++ dwc_frame_num_inc(qh->sched_frame, qh->interval); ++ if (dwc_frame_num_le(qh->sched_frame, frame_number)) { ++ qh->sched_frame = frame_number; ++ } ++ } ++ ++ if (DWC_CIRCLEQ_EMPTY(&qh->qtd_list)) { ++ dwc_otg_hcd_qh_remove(hcd, qh); ++ } else { ++ /* ++ * Remove from periodic_sched_queued and move to ++ * appropriate queue. ++ */ ++ if (qh->sched_frame == frame_number) { ++ DWC_LIST_MOVE_HEAD(&hcd->periodic_sched_ready, ++ &qh->qh_list_entry); ++ } else { ++ DWC_LIST_MOVE_HEAD(&hcd-> ++ periodic_sched_inactive, ++ &qh->qh_list_entry); ++ } ++ } ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++} ++ ++/** ++ * This function allocates and initializes a QTD. ++ * ++ * @param urb The URB to create a QTD from. Each URB-QTD pair will end up ++ * pointing to each other so each pair should have a unique correlation. ++ * ++ * @return Returns pointer to the newly allocated QTD, or NULL on error. */ ++dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(dwc_otg_hcd_urb_t * urb) ++{ ++ dwc_otg_qtd_t *qtd; ++ ++ qtd = dwc_otg_hcd_qtd_alloc(); ++ if (qtd == NULL) { ++ return NULL; ++ } ++ ++ dwc_otg_hcd_qtd_init(qtd, urb); ++ return qtd; ++} ++ ++/** ++ * Initializes a QTD structure. ++ * ++ * @param qtd The QTD to initialize. ++ * @param urb The URB to use for initialization. */ ++void dwc_otg_hcd_qtd_init(dwc_otg_qtd_t * qtd, dwc_otg_hcd_urb_t * urb) ++{ ++ dwc_memset(qtd, 0, sizeof(dwc_otg_qtd_t)); ++ qtd->urb = urb; ++ if (dwc_otg_hcd_get_pipe_type(&urb->pipe_info) == UE_CONTROL) { ++ /* ++ * The only time the QTD data toggle is used is on the data ++ * phase of control transfers. This phase always starts with ++ * DATA1. ++ */ ++ qtd->data_toggle = DWC_OTG_HC_PID_DATA1; ++ qtd->control_phase = DWC_OTG_CONTROL_SETUP; ++ } ++ ++ /* start split */ ++ qtd->complete_split = 0; ++ qtd->isoc_split_pos = DWC_HCSPLIT_XACTPOS_ALL; ++ qtd->isoc_split_offset = 0; ++ qtd->in_process = 0; ++ ++ /* Store the qtd ptr in the urb to reference what QTD. */ ++ urb->qtd = qtd; ++ return; ++} ++ ++/** ++ * This function adds a QTD to the QTD-list of a QH. It will find the correct ++ * QH to place the QTD into. If it does not find a QH, then it will create a ++ * new QH. If the QH to which the QTD is added is not currently scheduled, it ++ * is placed into the proper schedule based on its EP type. ++ * ++ * @param[in] qtd The QTD to add ++ * @param[in] hcd The DWC HCD structure ++ * @param[out] qh out parameter to return queue head ++ * ++ * @return 0 if successful, negative error code otherwise. ++ */ ++int dwc_otg_hcd_qtd_add(dwc_otg_qtd_t * qtd, ++ dwc_otg_hcd_t * hcd, dwc_otg_qh_t ** qh) ++{ ++ int retval = 0; ++ uint64_t flags; ++ ++ dwc_otg_hcd_urb_t *urb = qtd->urb; ++ ++ DWC_SPINLOCK_IRQSAVE(hcd->lock, &flags); ++ ++ /* ++ * Get the QH which holds the QTD-list to insert to. Create QH if it ++ * doesn't exist. ++ */ ++ if (*qh == NULL) { ++ *qh = dwc_otg_hcd_qh_create(hcd, urb); ++ if (*qh == NULL) { ++ retval = -1; ++ goto done; ++ } ++ } ++ ++ retval = dwc_otg_hcd_qh_add(hcd, *qh); ++ if (retval == 0) { ++ DWC_CIRCLEQ_INSERT_TAIL(&((*qh)->qtd_list), qtd, ++ qtd_list_entry); ++ } ++ ++ done: ++ DWC_SPINUNLOCK_IRQRESTORE(hcd->lock, flags); ++ ++ return retval; ++} ++ ++#endif /* DWC_DEVICE_ONLY */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd.c +@@ -0,0 +1,2067 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.c $ ++ * $Revision: #79 $ ++ * $Date: 2009/04/10 $ ++ * $Change: 1230501 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_HOST_ONLY ++ ++/** @file ++ * This file implements PCD Core. All code in this file is portable and don't ++ * use any OS specific functions. ++ * PCD Core provides Interface, defined in <code><dwc_otg_pcd_if.h></code> ++ * header file, which can be used to implement OS specific PCD interface. ++ * ++ * An important function of the PCD is managing interrupts generated ++ * by the DWC_otg controller. The implementation of the DWC_otg device ++ * mode interrupt service routines is in dwc_otg_pcd_intr.c. ++ * ++ * @todo Add Device Mode test modes (Test J mode, Test K mode, etc). ++ * @todo Does it work when the request size is greater than DEPTSIZ ++ * transfer size ++ * ++ */ ++ ++#include "dwc_otg_pcd.h" ++ ++#ifdef DWC_UTE_CFI ++#include "dwc_otg_cfi.h" ++ ++extern int init_cfi(cfiobject_t * cfiobj); ++#endif ++ ++static dwc_otg_pcd_ep_t *get_ep_from_handle(dwc_otg_pcd_t * pcd, void *handle) ++{ ++ int i; ++ if (pcd->ep0.priv == handle) { ++ return &pcd->ep0; ++ } ++ for (i = 0; i < MAX_EPS_CHANNELS - 1; i++) { ++ if (pcd->in_ep[i].priv == handle) ++ return &pcd->in_ep[i]; ++ if (pcd->out_ep[i].priv == handle) ++ return &pcd->out_ep[i]; ++ } ++ ++ return NULL; ++} ++ ++/** ++ * This function completes a request. It call's the request call back. ++ */ ++void dwc_otg_request_done(dwc_otg_pcd_ep_t * ep, dwc_otg_pcd_request_t * req, ++ int32_t status) ++{ ++ unsigned stopped = ep->stopped; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, ep); ++ DWC_CIRCLEQ_REMOVE_INIT(&ep->queue, req, queue_entry); ++ ++ /* don't modify queue heads during completion callback */ ++ ep->stopped = 1; ++ DWC_SPINUNLOCK(ep->pcd->lock); ++ ep->pcd->fops->complete(ep->pcd, ep->priv, req->priv, status, ++ req->actual); ++ DWC_SPINLOCK(ep->pcd->lock); ++ ++ if (ep->pcd->request_pending > 0) { ++ --ep->pcd->request_pending; ++ } ++ ++ ep->stopped = stopped; ++ dwc_free(req); ++} ++ ++/** ++ * This function terminates all the requsts in the EP request queue. ++ */ ++void dwc_otg_request_nuke(dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_otg_pcd_request_t *req; ++ ++ ep->stopped = 1; ++ ++ /* called with irqs blocked?? */ ++ while (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ dwc_otg_request_done(ep, req, -DWC_E_SHUTDOWN); ++ } ++} ++ ++void dwc_otg_pcd_start(dwc_otg_pcd_t * pcd, ++ const struct dwc_otg_pcd_function_ops *fops) ++{ ++ pcd->fops = fops; ++} ++ ++/** ++ * PCD Callback function for initializing the PCD when switching to ++ * device mode. ++ * ++ * @param p void pointer to the <code>dwc_otg_pcd_t</code> ++ */ ++static int32_t dwc_otg_pcd_start_cb(void *p) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p; ++ ++ /* ++ * Initialized the Core for Device mode. ++ */ ++ if (dwc_otg_is_device_mode(GET_CORE_IF(pcd))) { ++ dwc_otg_core_dev_init(GET_CORE_IF(pcd)); ++ } ++ return 1; ++} ++ ++/** CFI-specific buffer allocation function for EP */ ++#ifdef DWC_UTE_CFI ++uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, dwc_dma_t * addr, ++ size_t buflen, int flags) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ ep = get_ep_from_handle(pcd, pep); ++ return pcd->cfi->ops.ep_alloc_buf(pcd->cfi, pcd, ep, addr, buflen, ++ flags); ++} ++#else ++uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, dwc_dma_t * addr, ++ size_t buflen, int flags); ++#endif ++ ++/** ++ * PCD Callback function for notifying the PCD when resuming from ++ * suspend. ++ * ++ * @param p void pointer to the <code>dwc_otg_pcd_t</code> ++ */ ++static int32_t dwc_otg_pcd_resume_cb(void *p) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p; ++ ++ if (pcd->fops->resume) { ++ pcd->fops->resume(pcd); ++ } ++ ++ /* Stop the SRP timeout timer. */ ++ if ((GET_CORE_IF(pcd)->core_params->phy_type != DWC_PHY_TYPE_PARAM_FS) ++ || (!GET_CORE_IF(pcd)->core_params->i2c_enable)) { ++ if (GET_CORE_IF(pcd)->srp_timer_started) { ++ GET_CORE_IF(pcd)->srp_timer_started = 0; ++ DWC_TIMER_CANCEL(pcd->srp_timer); ++ } ++ } ++ return 1; ++} ++ ++/** ++ * PCD Callback function for notifying the PCD device is suspended. ++ * ++ * @param p void pointer to the <code>dwc_otg_pcd_t</code> ++ */ ++static int32_t dwc_otg_pcd_suspend_cb(void *p) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p; ++ ++ if (pcd->fops->suspend) { ++ pcd->fops->suspend(pcd); ++ } ++ ++ return 1; ++} ++ ++/** ++ * PCD Callback function for stopping the PCD when switching to Host ++ * mode. ++ * ++ * @param p void pointer to the <code>dwc_otg_pcd_t</code> ++ */ ++static int32_t dwc_otg_pcd_stop_cb(void *p) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) p; ++ extern void dwc_otg_pcd_stop(dwc_otg_pcd_t * _pcd); ++ ++ dwc_otg_pcd_stop(pcd); ++ return 1; ++} ++ ++/** ++ * PCD Callback structure for handling mode switching. ++ */ ++static dwc_otg_cil_callbacks_t pcd_callbacks = { ++ .start = dwc_otg_pcd_start_cb, ++ .stop = dwc_otg_pcd_stop_cb, ++ .suspend = dwc_otg_pcd_suspend_cb, ++ .resume_wakeup = dwc_otg_pcd_resume_cb, ++ .p = 0, /* Set at registration */ ++}; ++ ++/** ++ * This function allocates a DMA Descriptor chain for the Endpoint ++ * buffer to be used for a transfer to/from the specified endpoint. ++ */ ++dwc_otg_dev_dma_desc_t *dwc_otg_ep_alloc_desc_chain(uint32_t * dma_desc_addr, ++ uint32_t count) ++{ ++ ++ return dwc_dma_alloc(count * sizeof(dwc_otg_dev_dma_desc_t), dma_desc_addr); ++} ++ ++/** ++ * This function frees a DMA Descriptor chain that was allocated by ep_alloc_desc. ++ */ ++void dwc_otg_ep_free_desc_chain(dwc_otg_dev_dma_desc_t * desc_addr, ++ uint32_t dma_desc_addr, uint32_t count) ++{ ++ dwc_dma_free(count * sizeof(dwc_otg_dev_dma_desc_t), desc_addr, ++ dma_desc_addr); ++} ++ ++#ifdef DWC_EN_ISOC ++ ++/** ++ * This function initializes a descriptor chain for Isochronous transfer ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dwc_ep The EP to start the transfer on. ++ * ++ */ ++void dwc_otg_iso_ep_start_ddma_transfer(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * dwc_ep) ++{ ++ ++ dsts_data_t dsts = {.d32 = 0 }; ++ depctl_data_t depctl = {.d32 = 0 }; ++ volatile uint32_t *addr; ++ int i, j; ++ ++ if (dwc_ep->is_in) ++ dwc_ep->desc_cnt = dwc_ep->buf_proc_intrvl / dwc_ep->bInterval; ++ else ++ dwc_ep->desc_cnt = ++ dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / ++ dwc_ep->bInterval; ++ ++ /** Allocate descriptors for double buffering */ ++ dwc_ep->iso_desc_addr = ++ dwc_otg_ep_alloc_desc_chain(&dwc_ep->iso_dma_desc_addr, ++ dwc_ep->desc_cnt * 2); ++ if (dwc_ep->desc_addr) { ++ DWC_WARN("%s, can't allocate DMA descriptor chain\n", __func__); ++ return; ++ } ++ ++ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ /** ISO OUT EP */ ++ if (dwc_ep->is_in == 0) { ++ dev_dma_desc_sts_t sts = {.d32 = 0 }; ++ dwc_otg_dev_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr; ++ dma_addr_t dma_ad; ++ uint32_t data_per_desc; ++ dwc_otg_dev_out_ep_regs_t *out_regs = ++ core_if->dev_if->out_ep_regs[dwc_ep->num]; ++ int offset; ++ ++ addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl; ++ dma_ad = (dma_addr_t) dwc_read_reg32(&(out_regs->doepdma)); ++ ++ /** Buffer 0 descriptors setup */ ++ dma_ad = dwc_ep->dma_addr0; ++ ++ sts.b_iso_out.bs = BS_HOST_READY; ++ sts.b_iso_out.rxsts = 0; ++ sts.b_iso_out.l = 0; ++ sts.b_iso_out.sp = 0; ++ sts.b_iso_out.ioc = 0; ++ sts.b_iso_out.pid = 0; ++ sts.b_iso_out.framenum = 0; ++ ++ offset = 0; ++ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; ++ i += dwc_ep->pkt_per_frm) { ++ ++ for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep-> ++ data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - ++ data_per_desc % ++ 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ offset += data_per_desc; ++ dma_desc++; ++ dma_ad += data_per_desc; ++ } ++ } ++ ++ for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ offset += data_per_desc; ++ dma_desc++; ++ dma_ad += data_per_desc; ++ } ++ ++ sts.b_iso_out.ioc = 1; ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ dma_desc++; ++ ++ /** Buffer 1 descriptors setup */ ++ sts.b_iso_out.ioc = 0; ++ dma_ad = dwc_ep->dma_addr1; ++ ++ offset = 0; ++ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; ++ i += dwc_ep->pkt_per_frm) { ++ for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep-> ++ data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - ++ data_per_desc % ++ 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ offset += data_per_desc; ++ dma_desc++; ++ dma_ad += data_per_desc; ++ } ++ } ++ for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ offset += data_per_desc; ++ dma_desc++; ++ dma_ad += data_per_desc; ++ } ++ ++ sts.b_iso_out.ioc = 1; ++ sts.b_iso_out.l = 1; ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ dwc_ep->next_frame = 0; ++ ++ /** Write dma_ad into DOEPDMA register */ ++ dwc_write_reg32(&(out_regs->doepdma), ++ (uint32_t) dwc_ep->iso_dma_desc_addr); ++ ++ } ++ /** ISO IN EP */ ++ else { ++ dev_dma_desc_sts_t sts = {.d32 = 0 }; ++ dwc_otg_dev_dma_desc_t *dma_desc = dwc_ep->iso_desc_addr; ++ dma_addr_t dma_ad; ++ dwc_otg_dev_in_ep_regs_t *in_regs = ++ core_if->dev_if->in_ep_regs[dwc_ep->num]; ++ unsigned int frmnumber; ++ fifosize_data_t txfifosize, rxfifosize; ++ ++ txfifosize.d32 = ++ dwc_read_reg32(&core_if->dev_if->in_ep_regs[dwc_ep->num]-> ++ dtxfsts); ++ rxfifosize.d32 = ++ dwc_read_reg32(&core_if->core_global_regs->grxfsiz); ++ ++ addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl; ++ ++ dma_ad = dwc_ep->dma_addr0; ++ ++ dsts.d32 = ++ dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ sts.b_iso_in.bs = BS_HOST_READY; ++ sts.b_iso_in.txsts = 0; ++ sts.b_iso_in.sp = ++ (dwc_ep->data_per_frame % dwc_ep->maxpacket) ? 1 : 0; ++ sts.b_iso_in.ioc = 0; ++ sts.b_iso_in.pid = dwc_ep->pkt_per_frm; ++ ++ frmnumber = dwc_ep->next_frame; ++ ++ sts.b_iso_in.framenum = frmnumber; ++ sts.b_iso_in.txbytes = dwc_ep->data_per_frame; ++ sts.b_iso_in.l = 0; ++ ++ /** Buffer 0 descriptors setup */ ++ for (i = 0; i < dwc_ep->desc_cnt - 1; i++) { ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ dma_desc++; ++ ++ dma_ad += dwc_ep->data_per_frame; ++ sts.b_iso_in.framenum += dwc_ep->bInterval; ++ } ++ ++ sts.b_iso_in.ioc = 1; ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++dma_desc; ++ ++ /** Buffer 1 descriptors setup */ ++ sts.b_iso_in.ioc = 0; ++ dma_ad = dwc_ep->dma_addr1; ++ ++ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; ++ i += dwc_ep->pkt_per_frm) { ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ dma_desc++; ++ ++ dma_ad += dwc_ep->data_per_frame; ++ sts.b_iso_in.framenum += dwc_ep->bInterval; ++ ++ sts.b_iso_in.ioc = 0; ++ } ++ sts.b_iso_in.ioc = 1; ++ sts.b_iso_in.l = 1; ++ ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ dwc_ep->next_frame = sts.b_iso_in.framenum + dwc_ep->bInterval; ++ ++ /** Write dma_ad into diepdma register */ ++ dwc_write_reg32(&(in_regs->diepdma), ++ (uint32_t) dwc_ep->iso_dma_desc_addr); ++ } ++ /** Enable endpoint, clear nak */ ++ depctl.d32 = 0; ++ depctl.b.epena = 1; ++ depctl.b.usbactep = 1; ++ depctl.b.cnak = 1; ++ ++ dwc_modify_reg32(addr, depctl.d32, depctl.d32); ++ depctl.d32 = dwc_read_reg32(addr); ++} ++ ++/** ++ * This function initializes a descriptor chain for Isochronous transfer ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++ ++void dwc_otg_iso_ep_start_buf_transfer(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * ep) ++{ ++ depctl_data_t depctl = {.d32 = 0 }; ++ volatile uint32_t *addr; ++ ++ if (ep->is_in) { ++ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; ++ } else { ++ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; ++ } ++ ++ if (core_if->dma_enable == 0 || core_if->dma_desc_enable != 0) { ++ return; ++ } else { ++ deptsiz_data_t deptsiz = {.d32 = 0 }; ++ ++ ep->xfer_len = ++ ep->data_per_frame * ep->buf_proc_intrvl / ep->bInterval; ++ ep->pkt_cnt = ++ (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket; ++ ep->xfer_count = 0; ++ ep->xfer_buff = ++ (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0; ++ ep->dma_addr = ++ (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0; ++ ++ if (ep->is_in) { ++ /* Program the transfer size and packet count ++ * as follows: xfersize = N * maxpacket + ++ * short_packet pktcnt = N + (short_packet ++ * exist ? 1 : 0) ++ */ ++ deptsiz.b.mc = ep->pkt_per_frm; ++ deptsiz.b.xfersize = ep->xfer_len; ++ deptsiz.b.pktcnt = ++ (ep->xfer_len - 1 + ep->maxpacket) / ep->maxpacket; ++ dwc_write_reg32(&core_if->dev_if->in_ep_regs[ep->num]-> ++ dieptsiz, deptsiz.d32); ++ ++ /* Write the DMA register */ ++ dwc_write_reg32(& ++ (core_if->dev_if->in_ep_regs[ep->num]-> ++ diepdma), (uint32_t) ep->dma_addr); ++ ++ } else { ++ deptsiz.b.pktcnt = ++ (ep->xfer_len + (ep->maxpacket - 1)) / ++ ep->maxpacket; ++ deptsiz.b.xfersize = deptsiz.b.pktcnt * ep->maxpacket; ++ ++ dwc_write_reg32(&core_if->dev_if->out_ep_regs[ep->num]-> ++ doeptsiz, deptsiz.d32); ++ ++ /* Write the DMA register */ ++ dwc_write_reg32(& ++ (core_if->dev_if->out_ep_regs[ep->num]-> ++ doepdma), (uint32_t) ep->dma_addr); ++ ++ } ++ /** Enable endpoint, clear nak */ ++ depctl.d32 = 0; ++ dwc_modify_reg32(addr, depctl.d32, depctl.d32); ++ ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ ++ dwc_modify_reg32(addr, depctl.d32, depctl.d32); ++ } ++} ++ ++/** ++ * This function does the setup for a data transfer for an EP and ++ * starts the transfer. For an IN transfer, the packets will be ++ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers, ++ * the packets are unloaded from the Rx FIFO in the ISR. the ISR. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ */ ++ ++static void dwc_otg_iso_ep_start_transfer(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * ep) ++{ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable) { ++ if (ep->is_in) { ++ ep->desc_cnt = ep->pkt_cnt / ep->pkt_per_frm; ++ } else { ++ ep->desc_cnt = ep->pkt_cnt; ++ } ++ dwc_otg_iso_ep_start_ddma_transfer(core_if, ep); ++ } else { ++ if (core_if->pti_enh_enable) { ++ dwc_otg_iso_ep_start_buf_transfer(core_if, ep); ++ } else { ++ ep->cur_pkt_addr = ++ (ep->proc_buf_num) ? ep->xfer_buff1 : ep-> ++ xfer_buff0; ++ ep->cur_pkt_dma_addr = ++ (ep->proc_buf_num) ? ep->dma_addr1 : ep-> ++ dma_addr0; ++ dwc_otg_iso_ep_start_frm_transfer(core_if, ep); ++ } ++ } ++ } else { ++ ep->cur_pkt_addr = ++ (ep->proc_buf_num) ? ep->xfer_buff1 : ep->xfer_buff0; ++ ep->cur_pkt_dma_addr = ++ (ep->proc_buf_num) ? ep->dma_addr1 : ep->dma_addr0; ++ dwc_otg_iso_ep_start_frm_transfer(core_if, ep); ++ } ++} ++ ++/** ++ * This function does the setup for a data transfer for an EP and ++ * starts the transfer. For an IN transfer, the packets will be ++ * loaded into the appropriate Tx FIFO in the ISR. For OUT transfers, ++ * the packets are unloaded from the Rx FIFO in the ISR. the ISR. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ */ ++ ++void dwc_otg_iso_ep_stop_transfer(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ depctl_data_t depctl = {.d32 = 0 }; ++ volatile uint32_t *addr; ++ ++ if (ep->is_in == 1) { ++ addr = &core_if->dev_if->in_ep_regs[ep->num]->diepctl; ++ } else { ++ addr = &core_if->dev_if->out_ep_regs[ep->num]->doepctl; ++ } ++ ++ /* disable the ep */ ++ depctl.d32 = dwc_read_reg32(addr); ++ ++ depctl.b.epdis = 1; ++ depctl.b.snak = 1; ++ ++ dwc_write_reg32(addr, depctl.d32); ++ ++ if (core_if->dma_desc_enable && ++ ep->iso_desc_addr && ep->iso_dma_desc_addr) { ++ dwc_otg_ep_free_desc_chain(ep->iso_desc_addr, ++ ep->iso_dma_desc_addr, ++ ep->desc_cnt * 2); ++ } ++ ++ /* reset varibales */ ++ ep->dma_addr0 = 0; ++ ep->dma_addr1 = 0; ++ ep->xfer_buff0 = 0; ++ ep->xfer_buff1 = 0; ++ ep->data_per_frame = 0; ++ ep->data_pattern_frame = 0; ++ ep->sync_frame = 0; ++ ep->buf_proc_intrvl = 0; ++ ep->bInterval = 0; ++ ep->proc_buf_num = 0; ++ ep->pkt_per_frm = 0; ++ ep->pkt_per_frm = 0; ++ ep->desc_cnt = 0; ++ ep->iso_desc_addr = 0; ++ ep->iso_dma_desc_addr = 0; ++} ++ ++int dwc_otg_pcd_iso_ep_start(dwc_otg_pcd_t * pcd, void *ep_handle, ++ uint8_t * buf0, uint8_t * buf1, dwc_dma_t dma0, ++ dwc_dma_t dma1, int sync_frame, int dp_frame, ++ int data_per_frame, int start_frame, ++ int buf_proc_intrvl, void *req_handle, ++ int atomic_alloc) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ uint64_t flags = 0; ++ dwc_ep_t *dwc_ep; ++ int32_t frm_data; ++ dsts_data_t dsts; ++ dwc_otg_core_if_t *core_if; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ ++ if (!ep->desc || ep->dwc_ep.num == 0) { ++ DWC_WARN("bad ep\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ core_if = GET_CORE_IF(pcd); ++ dwc_ep = &ep->dwc_ep; ++ ++ if (ep->iso_req_handle) { ++ DWC_WARN("ISO request in progress\n"); ++ } ++ ++ dwc_ep->dma_addr0 = dma0; ++ dwc_ep->dma_addr1 = dma1; ++ ++ dwc_ep->xfer_buff0 = buf0; ++ dwc_ep->xfer_buff1 = buf1; ++ ++ dwc_ep->data_per_frame = data_per_frame; ++ ++ /** @todo - pattern data support is to be implemented in the future */ ++ dwc_ep->data_pattern_frame = dp_frame; ++ dwc_ep->sync_frame = sync_frame; ++ ++ dwc_ep->buf_proc_intrvl = buf_proc_intrvl; ++ ++ dwc_ep->bInterval = 1 << (ep->desc->bInterval - 1); ++ ++ dwc_ep->proc_buf_num = 0; ++ ++ dwc_ep->pkt_per_frm = 0; ++ frm_data = ep->dwc_ep.data_per_frame; ++ while (frm_data > 0) { ++ dwc_ep->pkt_per_frm++; ++ frm_data -= ep->dwc_ep.maxpacket; ++ } ++ ++ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ if (start_frame == -1) { ++ dwc_ep->next_frame = dsts.b.soffn + 1; ++ if (dwc_ep->bInterval != 1) { ++ dwc_ep->next_frame = ++ dwc_ep->next_frame + (dwc_ep->bInterval - 1 - ++ dwc_ep->next_frame % ++ dwc_ep->bInterval); ++ } ++ } else { ++ dwc_ep->next_frame = start_frame; ++ } ++ ++ if (!core_if->pti_enh_enable) { ++ dwc_ep->pkt_cnt = ++ dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / ++ dwc_ep->bInterval; ++ } else { ++ dwc_ep->pkt_cnt = ++ (dwc_ep->data_per_frame * ++ (dwc_ep->buf_proc_intrvl / dwc_ep->bInterval) ++ - 1 + dwc_ep->maxpacket) / dwc_ep->maxpacket; ++ } ++ ++ if (core_if->dma_desc_enable) { ++ dwc_ep->desc_cnt = ++ dwc_ep->buf_proc_intrvl * dwc_ep->pkt_per_frm / ++ dwc_ep->bInterval; ++ } ++ ++ if (atomic_alloc) { ++ dwc_ep->pkt_info = ++ dwc_alloc_atomic(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt); ++ } else { ++ dwc_ep->pkt_info = ++ dwc_alloc(sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt); ++ } ++ if (!dwc_ep->pkt_info) { ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ return -DWC_E_NO_MEMORY; ++ } ++ if (core_if->pti_enh_enable) { ++ dwc_memset(dwc_ep->pkt_info, 0, ++ sizeof(iso_pkt_info_t) * dwc_ep->pkt_cnt); ++ } ++ ++ dwc_ep->cur_pkt = 0; ++ ep->iso_req_handle = req_handle; ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ dwc_otg_iso_ep_start_transfer(core_if, dwc_ep); ++ return 0; ++} ++ ++int dwc_otg_pcd_iso_ep_stop(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle) ++{ ++ uint64_t flags = 0; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ if (!ep || !ep->desc || ep->dwc_ep.num == 0) { ++ DWC_WARN("bad ep\n"); ++ return -DWC_E_INVALID; ++ } ++ dwc_ep = &ep->dwc_ep; ++ ++ dwc_otg_iso_ep_stop_transfer(GET_CORE_IF(pcd), dwc_ep); ++ ++ dwc_free(dwc_ep->pkt_info); ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ if (ep->iso_req_handle != req_handle) { ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ return -DWC_E_INVALID; ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++ ep->iso_req_handle = 0; ++ return 0; ++} ++ ++/** ++ * This function is used for perodical data exchnage between PCD and gadget drivers. ++ * for Isochronous EPs ++ * ++ * - Every time a sync period completes this function is called to ++ * perform data exchange between PCD and gadget ++ */ ++void dwc_otg_iso_buffer_done(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep, ++ void *req_handle) ++{ ++ int i; ++ dwc_ep_t *dwc_ep; ++ ++ dwc_ep = &ep->dwc_ep; ++ ++ DWC_SPINUNLOCK(ep->pcd->lock); ++ pcd->fops->isoc_complete(pcd, ep->priv, ep->iso_req_handle, ++ dwc_ep->proc_buf_num ^ 0x1); ++ DWC_SPINLOCK(ep->pcd->lock); ++ ++ for (i = 0; i < dwc_ep->pkt_cnt; ++i) { ++ dwc_ep->pkt_info[i].status = 0; ++ dwc_ep->pkt_info[i].offset = 0; ++ dwc_ep->pkt_info[i].length = 0; ++ } ++} ++ ++int dwc_otg_pcd_get_iso_packet_count(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *iso_req_handle) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ dwc_ep = &ep->dwc_ep; ++ ++ return dwc_ep->pkt_cnt; ++} ++ ++void dwc_otg_pcd_get_iso_packet_params(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *iso_req_handle, int packet, ++ int *status, int *actual, int *offset) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ dwc_ep = &ep->dwc_ep; ++ ++ *status = dwc_ep->pkt_info[packet].status; ++ *actual = dwc_ep->pkt_info[packet].length; ++ *offset = dwc_ep->pkt_info[packet].offset; ++} ++ ++#endif /* DWC_EN_ISOC */ ++ ++static void dwc_otg_pcd_init_ep(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * pcd_ep, ++ uint32_t is_in, uint32_t ep_num) ++{ ++ /* Init EP structure */ ++ pcd_ep->desc = 0; ++ pcd_ep->pcd = pcd; ++ pcd_ep->stopped = 1; ++ pcd_ep->queue_sof = 0; ++ ++ /* Init DWC ep structure */ ++ pcd_ep->dwc_ep.is_in = is_in; ++ pcd_ep->dwc_ep.num = ep_num; ++ pcd_ep->dwc_ep.active = 0; ++ pcd_ep->dwc_ep.tx_fifo_num = 0; ++ /* Control until ep is actvated */ ++ pcd_ep->dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL; ++ pcd_ep->dwc_ep.maxpacket = MAX_PACKET_SIZE; ++ pcd_ep->dwc_ep.dma_addr = 0; ++ pcd_ep->dwc_ep.start_xfer_buff = 0; ++ pcd_ep->dwc_ep.xfer_buff = 0; ++ pcd_ep->dwc_ep.xfer_len = 0; ++ pcd_ep->dwc_ep.xfer_count = 0; ++ pcd_ep->dwc_ep.sent_zlp = 0; ++ pcd_ep->dwc_ep.total_len = 0; ++ pcd_ep->dwc_ep.desc_addr = 0; ++ pcd_ep->dwc_ep.dma_desc_addr = 0; ++ DWC_CIRCLEQ_INIT(&pcd_ep->queue); ++} ++ ++/** ++ * Initialise ep's ++ */ ++static void dwc_otg_pcd_reinit(dwc_otg_pcd_t * pcd) ++{ ++ int i; ++ uint32_t hwcfg1; ++ dwc_otg_pcd_ep_t *ep; ++ int in_ep_cntr, out_ep_cntr; ++ uint32_t num_in_eps = (GET_CORE_IF(pcd))->dev_if->num_in_eps; ++ uint32_t num_out_eps = (GET_CORE_IF(pcd))->dev_if->num_out_eps; ++ ++ /** ++ * Initialize the EP0 structure. ++ */ ++ ep = &pcd->ep0; ++ dwc_otg_pcd_init_ep(pcd, ep, 0, 0); ++ ++ in_ep_cntr = 0; ++ hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 3; ++ for (i = 1; in_ep_cntr < num_in_eps; i++) { ++ if ((hwcfg1 & 0x1) == 0) { ++ dwc_otg_pcd_ep_t *ep = &pcd->in_ep[in_ep_cntr]; ++ in_ep_cntr++; ++ /** ++ * @todo NGS: Add direction to EP, based on contents ++ * of HWCFG1. Need a copy of HWCFG1 in pcd structure? ++ * sprintf(";r ++ */ ++ dwc_otg_pcd_init_ep(pcd, ep, 1 /* IN */ , i); ++ ++ DWC_CIRCLEQ_INIT(&ep->queue); ++ } ++ hwcfg1 >>= 2; ++ } ++ ++ out_ep_cntr = 0; ++ hwcfg1 = (GET_CORE_IF(pcd))->hwcfg1.d32 >> 2; ++ for (i = 1; out_ep_cntr < num_out_eps; i++) { ++ if ((hwcfg1 & 0x1) == 0) { ++ dwc_otg_pcd_ep_t *ep = &pcd->out_ep[out_ep_cntr]; ++ out_ep_cntr++; ++ /** ++ * @todo NGS: Add direction to EP, based on contents ++ * of HWCFG1. Need a copy of HWCFG1 in pcd structure? ++ * sprintf(";r ++ */ ++ dwc_otg_pcd_init_ep(pcd, ep, 0 /* OUT */ , i); ++ DWC_CIRCLEQ_INIT(&ep->queue); ++ } ++ hwcfg1 >>= 2; ++ } ++ ++ pcd->ep0state = EP0_DISCONNECT; ++ pcd->ep0.dwc_ep.maxpacket = MAX_EP0_SIZE; ++ pcd->ep0.dwc_ep.type = DWC_OTG_EP_TYPE_CONTROL; ++} ++ ++/** ++ * This function is called when the SRP timer expires. The SRP should ++ * complete within 6 seconds. ++ */ ++static void srp_timeout(void *ptr) ++{ ++ gotgctl_data_t gotgctl; ++ dwc_otg_core_if_t *core_if = (dwc_otg_core_if_t *) ptr; ++ volatile uint32_t *addr = &core_if->core_global_regs->gotgctl; ++ ++ gotgctl.d32 = dwc_read_reg32(addr); ++ ++ core_if->srp_timer_started = 0; ++ ++ if ((core_if->core_params->phy_type == DWC_PHY_TYPE_PARAM_FS) && ++ (core_if->core_params->i2c_enable)) { ++ DWC_PRINTF("SRP Timeout\n"); ++ ++ if ((core_if->srp_success) && (gotgctl.b.bsesvld)) { ++ if (core_if->pcd_cb && core_if->pcd_cb->resume_wakeup) { ++ core_if->pcd_cb->resume_wakeup(core_if->pcd_cb-> ++ p); ++ } ++ ++ /* Clear Session Request */ ++ gotgctl.d32 = 0; ++ gotgctl.b.sesreq = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gotgctl, ++ gotgctl.d32, 0); ++ ++ core_if->srp_success = 0; ++ } else { ++ __DWC_ERROR("Device not connected/responding\n"); ++ gotgctl.b.sesreq = 0; ++ dwc_write_reg32(addr, gotgctl.d32); ++ } ++ } else if (gotgctl.b.sesreq) { ++ DWC_PRINTF("SRP Timeout\n"); ++ ++ __DWC_ERROR("Device not connected/responding\n"); ++ gotgctl.b.sesreq = 0; ++ dwc_write_reg32(addr, gotgctl.d32); ++ } else { ++ DWC_PRINTF(" SRP GOTGCTL=%0x\n", gotgctl.d32); ++ } ++} ++ ++/** ++ * Tasklet ++ * ++ */ ++extern void start_next_request(dwc_otg_pcd_ep_t * ep); ++ ++static void start_xfer_tasklet_func(void *data) ++{ ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) data; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ ++ int i; ++ depctl_data_t diepctl; ++ ++ DWC_DEBUGPL(DBG_PCDV, "Start xfer tasklet\n"); ++ ++ diepctl.d32 = dwc_read_reg32(&core_if->dev_if->in_ep_regs[0]->diepctl); ++ ++ if (pcd->ep0.queue_sof) { ++ pcd->ep0.queue_sof = 0; ++ start_next_request(&pcd->ep0); ++ // break; ++ } ++ ++ for (i = 0; i < core_if->dev_if->num_in_eps; i++) { ++ depctl_data_t diepctl; ++ diepctl.d32 = ++ dwc_read_reg32(&core_if->dev_if->in_ep_regs[i]->diepctl); ++ ++ if (pcd->in_ep[i].queue_sof) { ++ pcd->in_ep[i].queue_sof = 0; ++ start_next_request(&pcd->in_ep[i]); ++ // break; ++ } ++ } ++ ++ return; ++} ++ ++/** ++ * This function initialized the PCD portion of the driver. ++ * ++ */ ++dwc_otg_pcd_t *dwc_otg_pcd_init(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_pcd_t *pcd = 0; ++ dwc_otg_dev_if_t *dev_if; ++ ++ /* ++ * Allocate PCD structure ++ */ ++ pcd = dwc_alloc(sizeof(dwc_otg_pcd_t)); ++ ++ if (pcd == 0) { ++ return NULL; ++ } ++ ++ pcd->lock = DWC_SPINLOCK_ALLOC(); ++ DWC_DEBUGPL(DBG_HCDV, "Init of PCD %p given core_if %p\n", ++ pcd, core_if);//GRAYG ++ pcd->core_if = core_if; ++ if (!pcd->lock) { ++ DWC_ERROR("Could not allocate lock for pcd"); ++ dwc_free(pcd); ++ return NULL; ++ } ++ dev_if = core_if->dev_if; ++ ++ if (core_if->hwcfg4.b.ded_fifo_en) { ++ DWC_PRINTF("Dedicated Tx FIFOs mode\n"); ++ } else { ++ DWC_PRINTF("Shared Tx FIFO mode\n"); ++ } ++ ++ /* ++ * Initialized the Core for Device mode. ++ */ ++ if (dwc_otg_is_device_mode(core_if)) { ++ dwc_otg_core_dev_init(core_if); ++ } ++ ++ /* ++ * Register the PCD Callbacks. ++ */ ++ dwc_otg_cil_register_pcd_callbacks(core_if, &pcd_callbacks, pcd); ++ ++ /* ++ * Initialize the DMA buffer for SETUP packets ++ */ ++ if (GET_CORE_IF(pcd)->dma_enable) { ++ pcd->setup_pkt = ++ dwc_dma_alloc(sizeof(*pcd->setup_pkt) * 5, ++ &pcd->setup_pkt_dma_handle); ++ if (pcd->setup_pkt == 0) { ++ dwc_free(pcd); ++ return NULL; ++ } ++ ++ pcd->status_buf = ++ dwc_dma_alloc(sizeof(uint16_t), ++ &pcd->status_buf_dma_handle); ++ if (pcd->status_buf == 0) { ++ dwc_dma_free(sizeof(*pcd->setup_pkt) * 5, ++ pcd->setup_pkt, pcd->setup_pkt_dma_handle); ++ dwc_free(pcd); ++ return NULL; ++ } ++ ++ if (GET_CORE_IF(pcd)->dma_desc_enable) { ++ dev_if->setup_desc_addr[0] = ++ dwc_otg_ep_alloc_desc_chain(&dev_if-> ++ dma_setup_desc_addr[0], ++ 1); ++ dev_if->setup_desc_addr[1] = ++ dwc_otg_ep_alloc_desc_chain(&dev_if-> ++ dma_setup_desc_addr[1], ++ 1); ++ dev_if->in_desc_addr = ++ dwc_otg_ep_alloc_desc_chain(&dev_if-> ++ dma_in_desc_addr, 1); ++ dev_if->out_desc_addr = ++ dwc_otg_ep_alloc_desc_chain(&dev_if-> ++ dma_out_desc_addr, 1); ++ ++ if (dev_if->setup_desc_addr[0] == 0 ++ || dev_if->setup_desc_addr[1] == 0 ++ || dev_if->in_desc_addr == 0 ++ || dev_if->out_desc_addr == 0) { ++ ++ if (dev_if->out_desc_addr) ++ dwc_otg_ep_free_desc_chain(dev_if-> ++ out_desc_addr, ++ dev_if-> ++ dma_out_desc_addr, ++ 1); ++ if (dev_if->in_desc_addr) ++ dwc_otg_ep_free_desc_chain(dev_if-> ++ in_desc_addr, ++ dev_if-> ++ dma_in_desc_addr, ++ 1); ++ if (dev_if->setup_desc_addr[1]) ++ dwc_otg_ep_free_desc_chain(dev_if-> ++ setup_desc_addr ++ [1], ++ dev_if-> ++ dma_setup_desc_addr ++ [1], 1); ++ if (dev_if->setup_desc_addr[0]) ++ dwc_otg_ep_free_desc_chain(dev_if-> ++ setup_desc_addr ++ [0], ++ dev_if-> ++ dma_setup_desc_addr ++ [0], 1); ++ ++ dwc_dma_free(sizeof(*pcd->setup_pkt) * 5, ++ pcd->setup_pkt, ++ pcd->setup_pkt_dma_handle); ++ dwc_dma_free(sizeof(*pcd->status_buf), ++ pcd->status_buf, ++ pcd->status_buf_dma_handle); ++ ++ dwc_free(pcd); ++ ++ return NULL; ++ } ++ } ++ } else { ++ pcd->setup_pkt = dwc_alloc(sizeof(*pcd->setup_pkt) * 5); ++ if (pcd->setup_pkt == 0) { ++ dwc_free(pcd); ++ return NULL; ++ } ++ ++ pcd->status_buf = dwc_alloc(sizeof(uint16_t)); ++ if (pcd->status_buf == 0) { ++ dwc_free(pcd->setup_pkt); ++ dwc_free(pcd); ++ return NULL; ++ } ++ } ++ ++ dwc_otg_pcd_reinit(pcd); ++ ++ /* Allocate the cfi object for the PCD */ ++#ifdef DWC_UTE_CFI ++ pcd->cfi = dwc_alloc(sizeof(cfiobject_t)); ++ if (NULL == pcd->cfi) ++ return NULL; ++ if (init_cfi(pcd->cfi)) { ++ CFI_INFO("%s: Failed to init the CFI object\n", __func__); ++ return NULL; ++ } ++#endif ++ ++ /* Initialize tasklets */ ++ pcd->start_xfer_tasklet = DWC_TASK_ALLOC(start_xfer_tasklet_func, pcd); ++ pcd->test_mode_tasklet = DWC_TASK_ALLOC(do_test_mode, pcd); ++ /* Initialize timer */ ++ pcd->srp_timer = DWC_TIMER_ALLOC("SRP TIMER", srp_timeout, core_if); ++ return pcd; ++} ++ ++void dwc_otg_pcd_remove(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if; ++ ++ if (GET_CORE_IF(pcd)->dma_enable) { ++ dwc_dma_free(sizeof(*pcd->setup_pkt) * 5, pcd->setup_pkt, ++ pcd->setup_pkt_dma_handle); ++ dwc_dma_free(sizeof(uint16_t), pcd->status_buf, ++ pcd->status_buf_dma_handle); ++ if (GET_CORE_IF(pcd)->dma_desc_enable) { ++ dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[0], ++ dev_if-> ++ dma_setup_desc_addr[0], 1); ++ dwc_otg_ep_free_desc_chain(dev_if->setup_desc_addr[1], ++ dev_if-> ++ dma_setup_desc_addr[1], 1); ++ dwc_otg_ep_free_desc_chain(dev_if->in_desc_addr, ++ dev_if->dma_in_desc_addr, 1); ++ dwc_otg_ep_free_desc_chain(dev_if->out_desc_addr, ++ dev_if->dma_out_desc_addr, ++ 1); ++ } ++ } else { ++ dwc_free(pcd->setup_pkt); ++ dwc_free(pcd->status_buf); ++ } ++ DWC_SPINLOCK_FREE(pcd->lock); ++ DWC_TASK_FREE(pcd->start_xfer_tasklet); ++ DWC_TASK_FREE(pcd->test_mode_tasklet); ++ DWC_TIMER_FREE(pcd->srp_timer); ++ ++/* Release the CFI object's dynamic memory */ ++#ifdef DWC_UTE_CFI ++ if (pcd->cfi->ops.release) { ++ pcd->cfi->ops.release(pcd->cfi); ++ } ++#endif ++ ++ dwc_free(pcd); ++} ++ ++uint32_t dwc_otg_pcd_is_dualspeed(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ ++ if ((core_if->core_params->speed == DWC_SPEED_PARAM_FULL) || ++ ((core_if->hwcfg2.b.hs_phy_type == 2) && ++ (core_if->hwcfg2.b.fs_phy_type == 1) && ++ (core_if->core_params->ulpi_fs_ls))) { ++ return 0; ++ } ++ ++ return 1; ++} ++ ++uint32_t dwc_otg_pcd_is_otg(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ gusbcfg_data_t usbcfg = {.d32 = 0 }; ++ ++ usbcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->gusbcfg); ++ if (!usbcfg.b.srpcap || !usbcfg.b.hnpcap) { ++ return 0; ++ } ++ ++ return 1; ++} ++ ++/** ++ * This function assigns periodic Tx FIFO to an periodic EP ++ * in shared Tx FIFO mode ++ */ ++static uint32_t assign_tx_fifo(dwc_otg_core_if_t * core_if) ++{ ++ uint32_t TxMsk = 1; ++ int i; ++ ++ for (i = 0; i < core_if->hwcfg4.b.num_in_eps; ++i) { ++ if ((TxMsk & core_if->tx_msk) == 0) { ++ core_if->tx_msk |= TxMsk; ++ return i + 1; ++ } ++ TxMsk <<= 1; ++ } ++ return 0; ++} ++ ++/** ++ * This function assigns periodic Tx FIFO to an periodic EP ++ * in shared Tx FIFO mode ++ */ ++static uint32_t assign_perio_tx_fifo(dwc_otg_core_if_t * core_if) ++{ ++ uint32_t PerTxMsk = 1; ++ int i; ++ for (i = 0; i < core_if->hwcfg4.b.num_dev_perio_in_ep; ++i) { ++ if ((PerTxMsk & core_if->p_tx_msk) == 0) { ++ core_if->p_tx_msk |= PerTxMsk; ++ return i + 1; ++ } ++ PerTxMsk <<= 1; ++ } ++ return 0; ++} ++ ++/** ++ * This function releases periodic Tx FIFO ++ * in shared Tx FIFO mode ++ */ ++static void release_perio_tx_fifo(dwc_otg_core_if_t * core_if, ++ uint32_t fifo_num) ++{ ++ core_if->p_tx_msk = ++ (core_if->p_tx_msk & (1 << (fifo_num - 1))) ^ core_if->p_tx_msk; ++} ++ ++/** ++ * This function releases periodic Tx FIFO ++ * in shared Tx FIFO mode ++ */ ++static void release_tx_fifo(dwc_otg_core_if_t * core_if, uint32_t fifo_num) ++{ ++ core_if->tx_msk = ++ (core_if->tx_msk & (1 << (fifo_num - 1))) ^ core_if->tx_msk; ++} ++ ++int dwc_otg_pcd_ep_enable(dwc_otg_pcd_t * pcd, ++ const uint8_t * ep_desc, void *usb_ep) ++{ ++ int num, dir; ++ dwc_otg_pcd_ep_t *ep = 0; ++ const usb_endpoint_descriptor_t *desc; ++ uint64_t flags; ++ int retval = 0; ++ ++ desc = (const usb_endpoint_descriptor_t *)ep_desc; ++ ++ if (!desc) { ++ pcd->ep0.priv = usb_ep; ++ ep = &pcd->ep0; ++ retval = -DWC_E_INVALID; ++ goto out; ++ } ++ ++ num = UE_GET_ADDR(desc->bEndpointAddress); ++ dir = UE_GET_DIR(desc->bEndpointAddress); ++ ++ if (!desc->wMaxPacketSize) { ++ DWC_WARN("bad maxpacketsize\n"); ++ retval = -DWC_E_INVALID; ++ goto out; ++ } ++ ++ if (dir == UE_DIR_IN) { ++ ep = &pcd->in_ep[num - 1]; ++ } else { ++ ep = &pcd->out_ep[num - 1]; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ ++ ep->desc = desc; ++ ep->priv = usb_ep; ++ ++ /* ++ * Activate the EP ++ */ ++ ep->stopped = 0; ++ ++ ep->dwc_ep.is_in = (dir == UE_DIR_IN); ++ ep->dwc_ep.maxpacket = UGETW(desc->wMaxPacketSize); ++ ++ ep->dwc_ep.type = desc->bmAttributes & UE_XFERTYPE; ++ ++ if (ep->dwc_ep.is_in) { ++ if (!GET_CORE_IF(pcd)->en_multiple_tx_fifo) { ++ ep->dwc_ep.tx_fifo_num = 0; ++ ++ if (ep->dwc_ep.type == UE_ISOCHRONOUS) { ++ /* ++ * if ISOC EP then assign a Periodic Tx FIFO. ++ */ ++ ep->dwc_ep.tx_fifo_num = ++ assign_perio_tx_fifo(GET_CORE_IF(pcd)); ++ } ++ } else { ++ /* ++ * if Dedicated FIFOs mode is on then assign a Tx FIFO. ++ */ ++ ep->dwc_ep.tx_fifo_num = ++ assign_tx_fifo(GET_CORE_IF(pcd)); ++ ++ } ++ } ++ /* Set initial data PID. */ ++ if (ep->dwc_ep.type == UE_BULK) { ++ ep->dwc_ep.data_pid_start = 0; ++ } ++ ++ /* Alloc DMA Descriptors */ ++ if (GET_CORE_IF(pcd)->dma_desc_enable) { ++ if (ep->dwc_ep.type != UE_ISOCHRONOUS) { ++ ep->dwc_ep.desc_addr = ++ dwc_otg_ep_alloc_desc_chain(&ep->dwc_ep. ++ dma_desc_addr, ++ MAX_DMA_DESC_CNT); ++ if (!ep->dwc_ep.desc_addr) { ++ DWC_WARN("%s, can't allocate DMA descriptor\n", ++ __func__); ++ retval = -DWC_E_SHUTDOWN; ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ goto out; ++ } ++ } ++ } ++ ++ DWC_DEBUGPL(DBG_PCD, "Activate %s: type=%d, mps=%d desc=%p\n", ++ (ep->dwc_ep.is_in ? "IN" : "OUT"), ++ ep->dwc_ep.type, ep->dwc_ep.maxpacket, ep->desc); ++ ++ dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep); ++ ++#ifdef DWC_UTE_CFI ++ if (pcd->cfi->ops.ep_enable) { ++ pcd->cfi->ops.ep_enable(pcd->cfi, pcd, ep); ++ } ++#endif ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++ out: ++ return retval; ++} ++ ++int dwc_otg_pcd_ep_disable(dwc_otg_pcd_t * pcd, void *ep_handle) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ uint64_t flags; ++ dwc_otg_dev_dma_desc_t *desc_addr; ++ dwc_dma_t dma_desc_addr; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ ++ if (!ep || !ep->desc) { ++ DWC_DEBUGPL(DBG_PCD, "%s, %d %s not enabled\n", __func__, ++ ep->dwc_ep.num, ep->dwc_ep.is_in ? "IN" : "OUT"); ++ return -DWC_E_INVALID; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ ++ dwc_otg_request_nuke(ep); ++ ++ dwc_otg_ep_deactivate(GET_CORE_IF(pcd), &ep->dwc_ep); ++ ep->desc = 0; ++ ep->stopped = 1; ++ ++ if (ep->dwc_ep.is_in) { ++ dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num); ++ release_perio_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num); ++ release_tx_fifo(GET_CORE_IF(pcd), ep->dwc_ep.tx_fifo_num); ++ } ++ ++ /* Free DMA Descriptors */ ++ if (GET_CORE_IF(pcd)->dma_desc_enable) { ++ if (ep->dwc_ep.type != UE_ISOCHRONOUS) { ++ desc_addr = ep->dwc_ep.desc_addr; ++ dma_desc_addr = ep->dwc_ep.dma_desc_addr; ++ ++ /* Cannot call dma_free_coherent() with IRQs disabled */ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ dwc_otg_ep_free_desc_chain(desc_addr, dma_desc_addr, ++ MAX_DMA_DESC_CNT); ++ ++ goto out_unlocked; ++ } ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++ out_unlocked: ++ DWC_DEBUGPL(DBG_PCD, "%d %s disabled\n", ep->dwc_ep.num, ++ ep->dwc_ep.is_in ? "IN" : "OUT"); ++ return 0; ++ ++} ++ ++int dwc_otg_pcd_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle, ++ uint8_t * buf, dwc_dma_t dma_buf, uint32_t buflen, ++ int zero, void *req_handle, int atomic_alloc) ++{ ++ int prevented = 0; ++ uint64_t flags; ++ dwc_otg_pcd_request_t *req; ++ dwc_otg_pcd_ep_t *ep; ++ uint32_t max_transfer; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ if ((!ep->desc && ep->dwc_ep.num != 0)) { ++ DWC_WARN("bad ep\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ if (atomic_alloc) { ++ req = dwc_alloc_atomic(sizeof(*req)); ++ } else { ++ req = dwc_alloc(sizeof(*req)); ++ } ++ ++ if (!req) { ++ return -DWC_E_NO_MEMORY; ++ } ++ DWC_CIRCLEQ_INIT_ENTRY(req, queue_entry); ++ if (!GET_CORE_IF(pcd)->core_params->opt) { ++ if (ep->dwc_ep.num != 0) { ++ DWC_ERROR("queue req %p, len %d buf %p\n", ++ req_handle, buflen, buf); ++ } ++ } ++ ++ req->buf = buf; ++ req->dma = dma_buf; ++ req->length = buflen; ++ req->sent_zlp = zero; ++ req->priv = req_handle; ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ ++ /* ++ * For EP0 IN without premature status, zlp is required? ++ */ ++ if (ep->dwc_ep.num == 0 && ep->dwc_ep.is_in) { ++ DWC_DEBUGPL(DBG_PCDV, "%d-OUT ZLP\n", ep->dwc_ep.num); ++ //_req->zero = 1; ++ } ++ ++ /* Start the transfer */ ++ if (DWC_CIRCLEQ_EMPTY(&ep->queue) && !ep->stopped) { ++ /* EP0 Transfer? */ ++ if (ep->dwc_ep.num == 0) { ++ switch (pcd->ep0state) { ++ case EP0_IN_DATA_PHASE: ++ DWC_DEBUGPL(DBG_PCD, ++ "%s ep0: EP0_IN_DATA_PHASE\n", ++ __func__); ++ break; ++ ++ case EP0_OUT_DATA_PHASE: ++ DWC_DEBUGPL(DBG_PCD, ++ "%s ep0: EP0_OUT_DATA_PHASE\n", ++ __func__); ++ if (pcd->request_config) { ++ /* Complete STATUS PHASE */ ++ ep->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_STATUS_PHASE; ++ } ++ break; ++ ++ case EP0_IN_STATUS_PHASE: ++ DWC_DEBUGPL(DBG_PCD, ++ "%s ep0: EP0_IN_STATUS_PHASE\n", ++ __func__); ++ break; ++ ++ default: ++ DWC_DEBUGPL(DBG_ANY, "ep0: odd state %d\n", ++ pcd->ep0state); ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ return -DWC_E_SHUTDOWN; ++ } ++ ++ ep->dwc_ep.dma_addr = dma_buf; ++ ep->dwc_ep.start_xfer_buff = buf; ++ ep->dwc_ep.xfer_buff = buf; ++ ep->dwc_ep.xfer_len = buflen; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = ep->dwc_ep.xfer_len; ++ ++ if (zero) { ++ if ((ep->dwc_ep.xfer_len % ++ ep->dwc_ep.maxpacket == 0) ++ && (ep->dwc_ep.xfer_len != 0)) { ++ ep->dwc_ep.sent_zlp = 1; ++ } ++ ++ } ++ ++ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), ++ &ep->dwc_ep); ++ } // non-ep0 endpoints ++ else { ++#ifdef DWC_UTE_CFI ++ if (ep->dwc_ep.buff_mode != BM_STANDARD) { ++ /* store the request length */ ++ ep->dwc_ep.cfi_req_len = buflen; ++ pcd->cfi->ops.build_descriptors(pcd->cfi, pcd, ++ ep, req); ++ } else { ++#endif ++ max_transfer = ++ GET_CORE_IF(ep->pcd)->core_params-> ++ max_transfer_size; ++ ++ /* Setup and start the Transfer */ ++ ep->dwc_ep.dma_addr = dma_buf; ++ ep->dwc_ep.start_xfer_buff = buf; ++ ep->dwc_ep.xfer_buff = buf; ++ ep->dwc_ep.xfer_len = 0; ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = buflen; ++ ++ ep->dwc_ep.maxxfer = max_transfer; ++ if (GET_CORE_IF(pcd)->dma_desc_enable) { ++ uint32_t out_max_xfer = ++ DDMA_MAX_TRANSFER_SIZE - ++ (DDMA_MAX_TRANSFER_SIZE % 4); ++ if (ep->dwc_ep.is_in) { ++ if (ep->dwc_ep.maxxfer > ++ DDMA_MAX_TRANSFER_SIZE) { ++ ep->dwc_ep.maxxfer = ++ DDMA_MAX_TRANSFER_SIZE; ++ } ++ } else { ++ if (ep->dwc_ep.maxxfer > ++ out_max_xfer) { ++ ep->dwc_ep.maxxfer = ++ out_max_xfer; ++ } ++ } ++ } ++ if (ep->dwc_ep.maxxfer < ep->dwc_ep.total_len) { ++ ep->dwc_ep.maxxfer -= ++ (ep->dwc_ep.maxxfer % ++ ep->dwc_ep.maxpacket); ++ } ++ ++ if (zero) { ++ if ((ep->dwc_ep.total_len % ++ ep->dwc_ep.maxpacket == 0) ++ && (ep->dwc_ep.total_len != 0)) { ++ ep->dwc_ep.sent_zlp = 1; ++ } ++ } ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ dwc_otg_ep_start_transfer(GET_CORE_IF(pcd), ++ &ep->dwc_ep); ++ } ++ } ++ ++ if ((req != 0) || prevented) { ++ ++pcd->request_pending; ++ DWC_CIRCLEQ_INSERT_TAIL(&ep->queue, req, queue_entry); ++ if (ep->dwc_ep.is_in && ep->stopped ++ && !(GET_CORE_IF(pcd)->dma_enable)) { ++ /** @todo NGS Create a function for this. */ ++ diepmsk_data_t diepmsk = {.d32 = 0 }; ++ diepmsk.b.intktxfemp = 1; ++ if (GET_CORE_IF(pcd)->multiproc_int_enable) { ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->dev_if-> ++ dev_global_regs-> ++ diepeachintmsk[ep->dwc_ep.num], ++ 0, diepmsk.d32); ++ } else { ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->dev_if-> ++ dev_global_regs->diepmsk, 0, ++ diepmsk.d32); ++ } ++ ++ } ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++ return 0; ++} ++int dwc_otg_pcd_ep_dequeue(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle) ++{ ++ uint64_t flags; ++ dwc_otg_pcd_request_t *req; ++ dwc_otg_pcd_ep_t *ep; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ if (!ep->desc && ep->dwc_ep.num != 0) { ++ DWC_WARN("bad argument\n"); ++ return -DWC_E_INVALID; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ ++ /* make sure it's actually queued on this endpoint */ ++ DWC_CIRCLEQ_FOREACH(req, &ep->queue, queue_entry) { ++ if (req->priv == (void *)req_handle) { ++ break; ++ } ++ } ++ ++ if (req->priv != (void *)req_handle) { ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ return -DWC_E_INVALID; ++ } ++ ++ if (!DWC_CIRCLEQ_EMPTY_ENTRY(req, queue_entry)) { ++ dwc_otg_request_done(ep, req, -DWC_E_RESTART); ++ } else { ++ req = 0; ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++ return req ? 0 : -DWC_E_SHUTDOWN; ++ ++} ++ ++/** ++ * dwc_otg_pcd_ep_wedge - sets the halt feature and ignores clear requests ++ * ++ * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT) ++ * requests. If the gadget driver clears the halt status, it will ++ * automatically unwedge the endpoint. ++ * ++ * Returns zero on success, else negative DWC error code. ++ */ ++int dwc_otg_pcd_ep_wedge(dwc_otg_pcd_t * pcd, void *ep_handle) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ uint64_t flags; ++ int retval = 0; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ ++ if ((!ep->desc && ep != &pcd->ep0) || ++ (ep->desc && (ep->desc->bmAttributes == UE_ISOCHRONOUS))) { ++ DWC_WARN("%s, bad ep\n", __func__); ++ return -DWC_E_INVALID; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ DWC_WARN("%d %s XFer In process\n", ep->dwc_ep.num, ++ ep->dwc_ep.is_in ? "IN" : "OUT"); ++ retval = -DWC_E_AGAIN; ++ } else { ++ /* This code needs to be reviewed */ ++ if (ep->dwc_ep.is_in == 1 && GET_CORE_IF(pcd)->dma_desc_enable) { ++ dtxfsts_data_t txstatus; ++ fifosize_data_t txfifosize; ++ ++ txfifosize.d32 = ++ dwc_read_reg32(&GET_CORE_IF(pcd)->core_global_regs-> ++ dptxfsiz_dieptxf[ep->dwc_ep. ++ tx_fifo_num]); ++ txstatus.d32 = ++ dwc_read_reg32(&GET_CORE_IF(pcd)->dev_if-> ++ in_ep_regs[ep->dwc_ep.num]->dtxfsts); ++ ++ if (txstatus.b.txfspcavail < txfifosize.b.depth) { ++ DWC_WARN("%s() Data In Tx Fifo\n", __func__); ++ retval = -DWC_E_AGAIN; ++ } else { ++ if (ep->dwc_ep.num == 0) { ++ pcd->ep0state = EP0_STALL; ++ } ++ ++ ep->stopped = 1; ++ dwc_otg_ep_set_stall(GET_CORE_IF(pcd), ++ &ep->dwc_ep); ++ } ++ } else { ++ if (ep->dwc_ep.num == 0) { ++ pcd->ep0state = EP0_STALL; ++ } ++ ++ ep->stopped = 1; ++ dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep->dwc_ep); ++ } ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++ return retval; ++} ++ ++int dwc_otg_pcd_ep_halt(dwc_otg_pcd_t * pcd, void *ep_handle, int value) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ uint64_t flags; ++ int retval = 0; ++ ++ ep = get_ep_from_handle(pcd, ep_handle); ++ ++ if ((!ep->desc && ep != &pcd->ep0) || ++ (ep->desc && (ep->desc->bmAttributes == UE_ISOCHRONOUS))) { ++ DWC_WARN("%s, bad ep\n", __func__); ++ return -DWC_E_INVALID; ++ } ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ DWC_WARN("%d %s XFer In process\n", ep->dwc_ep.num, ++ ep->dwc_ep.is_in ? "IN" : "OUT"); ++ retval = -DWC_E_AGAIN; ++ } else if (value == 0) { ++ dwc_otg_ep_clear_stall(GET_CORE_IF(pcd), &ep->dwc_ep); ++ } else if (value == 1) { ++ if (ep->dwc_ep.is_in == 1 && GET_CORE_IF(pcd)->dma_desc_enable) { ++ dtxfsts_data_t txstatus; ++ fifosize_data_t txfifosize; ++ ++ txfifosize.d32 = ++ dwc_read_reg32(&GET_CORE_IF(pcd)->core_global_regs-> ++ dptxfsiz_dieptxf[ep->dwc_ep. ++ tx_fifo_num]); ++ txstatus.d32 = ++ dwc_read_reg32(&GET_CORE_IF(pcd)->dev_if-> ++ in_ep_regs[ep->dwc_ep.num]->dtxfsts); ++ ++ if (txstatus.b.txfspcavail < txfifosize.b.depth) { ++ DWC_WARN("%s() Data In Tx Fifo\n", __func__); ++ retval = -DWC_E_AGAIN; ++ } else { ++ if (ep->dwc_ep.num == 0) { ++ pcd->ep0state = EP0_STALL; ++ } ++ ++ ep->stopped = 1; ++ dwc_otg_ep_set_stall(GET_CORE_IF(pcd), ++ &ep->dwc_ep); ++ } ++ } else { ++ if (ep->dwc_ep.num == 0) { ++ pcd->ep0state = EP0_STALL; ++ } ++ ++ ep->stopped = 1; ++ dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep->dwc_ep); ++ } ++ } else if (value == 2) { ++ ep->dwc_ep.stall_clear_flag = 0; ++ } else if (value == 3) { ++ ep->dwc_ep.stall_clear_flag = 1; ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ ++ return retval; ++} ++ ++/** ++ * This function initiates remote wakeup of the host from suspend state. ++ */ ++void dwc_otg_pcd_rem_wkup_from_suspend(dwc_otg_pcd_t * pcd, int set) ++{ ++ dctl_data_t dctl = { 0 }; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dsts_data_t dsts; ++ ++ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ if (!dsts.b.suspsts) { ++ DWC_WARN("Remote wakeup while is not in suspend state\n"); ++ } ++ /* Check if DEVICE_REMOTE_WAKEUP feature enabled */ ++ if (pcd->remote_wakeup_enable) { ++ if (set) { ++ dctl.b.rmtwkupsig = 1; ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs-> ++ dctl, 0, dctl.d32); ++ DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n"); ++ dwc_mdelay(2); ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs-> ++ dctl, dctl.d32, 0); ++ DWC_DEBUGPL(DBG_PCD, "Clear Remote Wakeup\n"); ++ } ++ } else { ++ DWC_DEBUGPL(DBG_PCD, "Remote Wakeup is disabled\n"); ++ } ++} ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++/** ++ * This function initiates remote wakeup of the host from L1 sleep state. ++ */ ++void dwc_otg_pcd_rem_wkup_from_sleep(dwc_otg_pcd_t * pcd, int set) ++{ ++ glpmcfg_data_t lpmcfg; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ ++ lpmcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ ++ /* Check if we are in L1 state */ ++ if (!lpmcfg.b.prt_sleep_sts) { ++ DWC_DEBUGPL(DBG_PCD, "Device is not in sleep state\n"); ++ return; ++ } ++ ++ /* Check if host allows remote wakeup */ ++ if (!lpmcfg.b.rem_wkup_en) { ++ DWC_DEBUGPL(DBG_PCD, "Host does not allow remote wakeup\n"); ++ return; ++ } ++ ++ /* Check if Resume OK */ ++ if (!lpmcfg.b.sleep_state_resumeok) { ++ DWC_DEBUGPL(DBG_PCD, "Sleep state resume is not OK\n"); ++ return; ++ } ++ ++ lpmcfg.d32 = dwc_read_reg32(&core_if->core_global_regs->glpmcfg); ++ lpmcfg.b.en_utmi_sleep = 0; ++ lpmcfg.b.hird_thres &= (~(1 << 4)); ++ dwc_write_reg32(&core_if->core_global_regs->glpmcfg, lpmcfg.d32); ++ ++ if (set) { ++ dctl_data_t dctl = {.d32 = 0 }; ++ dctl.b.rmtwkupsig = 1; ++ /* Set RmtWkUpSig bit to start remote wakup signaling. ++ * Hardware will automatically clear this bit. ++ */ ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dctl, ++ 0, dctl.d32); ++ DWC_DEBUGPL(DBG_PCD, "Set Remote Wakeup\n"); ++ } ++ ++} ++#endif ++ ++/** ++ * Performs remote wakeup. ++ */ ++void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t * pcd, int set) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ if (dwc_otg_is_device_mode(core_if)) { ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ if (core_if->lx_state == DWC_OTG_L1) { ++ dwc_otg_pcd_rem_wkup_from_sleep(pcd, set); ++ } else { ++#endif ++ dwc_otg_pcd_rem_wkup_from_suspend(pcd, set); ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ } ++#endif ++ } ++ return; ++} ++ ++int dwc_otg_pcd_wakeup(dwc_otg_pcd_t * pcd) ++{ ++ dsts_data_t dsts; ++ gotgctl_data_t gotgctl; ++ uint64_t flags; ++ ++ DWC_SPINLOCK_IRQSAVE(pcd->lock, &flags); ++ ++ /* ++ * This function starts the Protocol if no session is in progress. If ++ * a session is already in progress, but the device is suspended, ++ * remote wakeup signaling is started. ++ */ ++ ++ /* Check if valid session */ ++ gotgctl.d32 = ++ dwc_read_reg32(&(GET_CORE_IF(pcd)->core_global_regs->gotgctl)); ++ if (gotgctl.b.bsesvld) { ++ /* Check if suspend state */ ++ dsts.d32 = ++ dwc_read_reg32(& ++ (GET_CORE_IF(pcd)->dev_if->dev_global_regs-> ++ dsts)); ++ if (dsts.b.suspsts) { ++ dwc_otg_pcd_remote_wakeup(pcd, 1); ++ } ++ } else { ++ dwc_otg_pcd_initiate_srp(pcd); ++ } ++ ++ DWC_SPINUNLOCK_IRQRESTORE(pcd->lock, flags); ++ return 0; ++ ++} ++ ++/** ++ * Start the SRP timer to detect when the SRP does not complete within ++ * 6 seconds. ++ * ++ * @param pcd the pcd structure. ++ */ ++void dwc_otg_pcd_start_srp_timer(dwc_otg_pcd_t * pcd) ++{ ++ GET_CORE_IF(pcd)->srp_timer_started = 1; ++ DWC_TIMER_SCHEDULE(pcd->srp_timer, 6000 /* 6 secs */ ); ++} ++ ++void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t * pcd) ++{ ++ uint32_t *addr = ++ (uint32_t *) & (GET_CORE_IF(pcd)->core_global_regs->gotgctl); ++ gotgctl_data_t mem; ++ gotgctl_data_t val; ++ ++ val.d32 = dwc_read_reg32(addr); ++ if (val.b.sesreq) { ++ DWC_ERROR("Session Request Already active!\n"); ++ return; ++ } ++ ++ DWC_INFO("Session Request Initated\n"); //NOTICE ++ mem.d32 = dwc_read_reg32(addr); ++ mem.b.sesreq = 1; ++ dwc_write_reg32(addr, mem.d32); ++ ++ /* Start the SRP timer */ ++ dwc_otg_pcd_start_srp_timer(pcd); ++ return; ++} ++ ++int dwc_otg_pcd_get_frame_number(dwc_otg_pcd_t * pcd) ++{ ++ return dwc_otg_get_frame_number(GET_CORE_IF(pcd)); ++} ++ ++int dwc_otg_pcd_is_lpm_enabled(dwc_otg_pcd_t * pcd) ++{ ++ return GET_CORE_IF(pcd)->core_params->lpm_enable; ++} ++ ++uint32_t get_b_hnp_enable(dwc_otg_pcd_t * pcd) ++{ ++ return pcd->b_hnp_enable; ++} ++ ++uint32_t get_a_hnp_support(dwc_otg_pcd_t * pcd) ++{ ++ return pcd->a_hnp_support; ++} ++ ++uint32_t get_a_alt_hnp_support(dwc_otg_pcd_t * pcd) ++{ ++ return pcd->a_alt_hnp_support; ++} ++ ++int dwc_otg_pcd_get_rmwkup_enable(dwc_otg_pcd_t * pcd) ++{ ++ return pcd->remote_wakeup_enable; ++} ++ ++#endif /* DWC_HOST_ONLY */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd.h +@@ -0,0 +1,216 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd.h $ ++ * $Revision: #39 $ ++ * $Date: 2008/12/16 $ ++ * $Change: 1153731 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_HOST_ONLY ++#if !defined(__DWC_PCD_H__) ++#define __DWC_PCD_H__ ++ ++#include "usb.h" ++#include "dwc_otg_cil.h" ++#include "dwc_otg_pcd_if.h" ++struct cfiobject; ++ ++/** ++ * @file ++ * ++ * This file contains the structures, constants, and interfaces for ++ * the Perpherial Contoller Driver (PCD). ++ * ++ * The Peripheral Controller Driver (PCD) for Linux will implement the ++ * Gadget API, so that the existing Gadget drivers can be used. For ++ * the Mass Storage Function driver the File-backed USB Storage Gadget ++ * (FBS) driver will be used. The FBS driver supports the ++ * Control-Bulk (CB), Control-Bulk-Interrupt (CBI), and Bulk-Only ++ * transports. ++ * ++ */ ++ ++/** Max Transfer size for any EP */ ++#define DDMA_MAX_TRANSFER_SIZE 65535 ++ ++/** Max DMA Descriptor count for any EP */ ++#define MAX_DMA_DESC_CNT 64 ++ ++/** ++ * Get the pointer to the core_if from the pcd pointer. ++ */ ++#define GET_CORE_IF( _pcd ) (_pcd->core_if) ++ ++/** ++ * States of EP0. ++ */ ++typedef enum ep0_state { ++ EP0_DISCONNECT, /* no host */ ++ EP0_IDLE, ++ EP0_IN_DATA_PHASE, ++ EP0_OUT_DATA_PHASE, ++ EP0_IN_STATUS_PHASE, ++ EP0_OUT_STATUS_PHASE, ++ EP0_STALL, ++} ep0state_e; ++ ++/** Fordward declaration.*/ ++struct dwc_otg_pcd; ++ ++/** DWC_otg iso request structure. ++ * ++ */ ++typedef struct usb_iso_request dwc_otg_pcd_iso_request_t; ++ ++/** DWC_otg request structure. ++ * This structure is a list of requests. ++ */ ++typedef struct dwc_otg_pcd_request { ++ void *priv; ++ void *buf; ++ dwc_dma_t dma; ++ uint32_t length; ++ uint32_t actual; ++ unsigned sent_zlp:1; ++ ++ DWC_CIRCLEQ_ENTRY(dwc_otg_pcd_request) queue_entry; ++} dwc_otg_pcd_request_t; ++ ++DWC_CIRCLEQ_HEAD(req_list, dwc_otg_pcd_request); ++ ++/** PCD EP structure. ++ * This structure describes an EP, there is an array of EPs in the PCD ++ * structure. ++ */ ++typedef struct dwc_otg_pcd_ep { ++ /** USB EP Descriptor */ ++ const usb_endpoint_descriptor_t *desc; ++ ++ /** queue of dwc_otg_pcd_requests. */ ++ struct req_list queue; ++ unsigned stopped:1; ++ unsigned disabling:1; ++ unsigned dma:1; ++ unsigned queue_sof:1; ++ ++#ifdef DWC_EN_ISOC ++ /** ISOC req handle passed */ ++ void *iso_req_handle; ++#endif //_EN_ISOC_ ++ ++ /** DWC_otg ep data. */ ++ dwc_ep_t dwc_ep; ++ ++ /** Pointer to PCD */ ++ struct dwc_otg_pcd *pcd; ++ ++ void *priv; ++} dwc_otg_pcd_ep_t; ++ ++/** DWC_otg PCD Structure. ++ * This structure encapsulates the data for the dwc_otg PCD. ++ */ ++struct dwc_otg_pcd { ++ const struct dwc_otg_pcd_function_ops *fops; ++ /** Core Interface */ ++ dwc_otg_core_if_t *core_if; ++ /** State of EP0 */ ++ ep0state_e ep0state; ++ /** EP0 Request is pending */ ++ unsigned ep0_pending:1; ++ /** Indicates when SET CONFIGURATION Request is in process */ ++ unsigned request_config:1; ++ /** The state of the Remote Wakeup Enable. */ ++ unsigned remote_wakeup_enable:1; ++ /** The state of the B-Device HNP Enable. */ ++ unsigned b_hnp_enable:1; ++ /** The state of A-Device HNP Support. */ ++ unsigned a_hnp_support:1; ++ /** The state of the A-Device Alt HNP support. */ ++ unsigned a_alt_hnp_support:1; ++ /** Count of pending Requests */ ++ unsigned request_pending; ++ ++ /** SETUP packet for EP0 ++ * This structure is allocated as a DMA buffer on PCD initialization ++ * with enough space for up to 3 setup packets. ++ */ ++ union { ++ usb_device_request_t req; ++ uint32_t d32[2]; ++ } *setup_pkt; ++ ++ dwc_dma_t setup_pkt_dma_handle; ++ ++ /** 2-byte dma buffer used to return status from GET_STATUS */ ++ uint16_t *status_buf; ++ dwc_dma_t status_buf_dma_handle; ++ ++ /** EP0 */ ++ dwc_otg_pcd_ep_t ep0; ++ ++ /** Array of IN EPs. */ ++ dwc_otg_pcd_ep_t in_ep[MAX_EPS_CHANNELS - 1]; ++ /** Array of OUT EPs. */ ++ dwc_otg_pcd_ep_t out_ep[MAX_EPS_CHANNELS - 1]; ++ /** number of valid EPs in the above array. */ ++// unsigned num_eps : 4; ++ dwc_spinlock_t *lock; ++ /** Timer for SRP. If it expires before SRP is successful ++ * clear the SRP. */ ++ dwc_timer_t *srp_timer; ++ ++ /** Tasklet to defer starting of TEST mode transmissions until ++ * Status Phase has been completed. ++ */ ++ dwc_tasklet_t *test_mode_tasklet; ++ ++ /** Tasklet to delay starting of xfer in DMA mode */ ++ dwc_tasklet_t *start_xfer_tasklet; ++ ++ /** The test mode to enter when the tasklet is executed. */ ++ unsigned test_mode; ++ /** The cfi_api structure that implements most of the CFI API ++ * and OTG specific core configuration functionality ++ */ ++#ifdef DWC_UTE_CFI ++ struct cfiobject *cfi; ++#endif ++ ++}; ++ ++//FIXME this functions should be static, and this prototypes should be removed ++extern void dwc_otg_request_nuke(dwc_otg_pcd_ep_t * ep); ++extern void dwc_otg_request_done(dwc_otg_pcd_ep_t * ep, ++ dwc_otg_pcd_request_t * req, int32_t status); ++ ++void dwc_otg_iso_buffer_done(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep, ++ void *req_handle); ++ ++extern void do_test_mode(void *data); ++#endif ++#endif /* DWC_HOST_ONLY */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_if.h +@@ -0,0 +1,333 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_if.h $ ++ * $Revision: #6 $ ++ * $Date: 2009/04/03 $ ++ * $Change: 1225059 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_HOST_ONLY ++ ++#if !defined(__DWC_PCD_IF_H__) ++#define __DWC_PCD_IF_H__ ++ ++#include "dwc_os.h" ++#include "dwc_otg_core_if.h" ++ ++/** @file ++ * This file defines DWC_OTG PCD Core API. ++ */ ++ ++struct dwc_otg_pcd; ++typedef struct dwc_otg_pcd dwc_otg_pcd_t; ++ ++/** Maxpacket size for EP0 */ ++#define MAX_EP0_SIZE 64 ++/** Maxpacket size for any EP */ ++#define MAX_PACKET_SIZE 1024 ++ ++/** @name Function Driver Callbacks */ ++/** @{ */ ++ ++/** This function will be called whenever a previously queued request has ++ * completed. The status value will be set to -DWC_E_SHUTDOWN to indicated a ++ * failed or aborted transfer, or -DWC_E_RESTART to indicate the device was reset, ++ * or -DWC_E_TIMEOUT to indicate it timed out, or -DWC_E_INVALID to indicate invalid ++ * parameters. */ ++typedef int (*dwc_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle, int32_t status, ++ uint32_t actual); ++/** ++ * This function will be called whenever a previousle queued ISOC request has ++ * completed. Count of ISOC packets could be read using dwc_otg_pcd_get_iso_packet_count ++ * function. ++ * The status of each ISOC packet could be read using dwc_otg_pcd_get_iso_packet_* ++ * functions. ++ */ ++typedef int (*dwc_isoc_completion_cb_t) (dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle, int proc_buf_num); ++/** This function should handle any SETUP request that cannot be handled by the ++ * PCD Core. This includes most GET_DESCRIPTORs, SET_CONFIGS, Any ++ * class-specific requests, etc. The function must non-blocking. ++ * ++ * Returns 0 on success. ++ * Returns -DWC_E_NOT_SUPPORTED if the request is not supported. ++ * Returns -DWC_E_INVALID if the setup request had invalid parameters or bytes. ++ * Returns -DWC_E_SHUTDOWN on any other error. */ ++typedef int (*dwc_setup_cb_t) (dwc_otg_pcd_t * pcd, uint8_t * bytes); ++/** This is called whenever the device has been disconnected. The function ++ * driver should take appropriate action to clean up all pending requests in the ++ * PCD Core, remove all endpoints (except ep0), and initialize back to reset ++ * state. */ ++typedef int (*dwc_disconnect_cb_t) (dwc_otg_pcd_t * pcd); ++/** This function is called when device has been connected. */ ++typedef int (*dwc_connect_cb_t) (dwc_otg_pcd_t * pcd, int speed); ++/** This function is called when device has been suspended */ ++typedef int (*dwc_suspend_cb_t) (dwc_otg_pcd_t * pcd); ++/** This function is called when device has received LPM tokens, i.e. ++ * device has been sent to sleep state. */ ++typedef int (*dwc_sleep_cb_t) (dwc_otg_pcd_t * pcd); ++/** This function is called when device has been resumed ++ * from suspend(L2) or L1 sleep state. */ ++typedef int (*dwc_resume_cb_t) (dwc_otg_pcd_t * pcd); ++/** This function is called whenever hnp params has been changed. ++ * User can call get_b_hnp_enable, get_a_hnp_support, get_a_alt_hnp_support functions ++ * to get hnp parameters. */ ++typedef int (*dwc_hnp_params_changed_cb_t) (dwc_otg_pcd_t * pcd); ++/** This function is called whenever USB RESET is detected. */ ++typedef int (*dwc_reset_cb_t) (dwc_otg_pcd_t * pcd); ++ ++typedef int (*cfi_setup_cb_t) (dwc_otg_pcd_t * pcd, void *ctrl_req_bytes); ++ ++/** Function Driver Ops Data Structure */ ++struct dwc_otg_pcd_function_ops { ++ dwc_connect_cb_t connect; ++ dwc_disconnect_cb_t disconnect; ++ dwc_setup_cb_t setup; ++ dwc_completion_cb_t complete; ++ dwc_isoc_completion_cb_t isoc_complete; ++ dwc_suspend_cb_t suspend; ++ dwc_sleep_cb_t sleep; ++ dwc_resume_cb_t resume; ++ dwc_reset_cb_t reset; ++ dwc_hnp_params_changed_cb_t hnp_changed; ++ cfi_setup_cb_t cfi_setup; ++}; ++/** @} */ ++ ++/** @name Function Driver Functions */ ++/** @{ */ ++ ++/** Call this function to get pointer on dwc_otg_pcd_t, ++ * this pointer will be used for all PCD API functions. ++ * ++ * @param core_if The DWC_OTG Core ++ */ ++extern dwc_otg_pcd_t *dwc_otg_pcd_init(dwc_otg_core_if_t * core_if); ++ ++/** Frees PCD allocated by dwc_otg_pcd_init ++ * ++ * @param pcd The PCD ++ */ ++extern void dwc_otg_pcd_remove(dwc_otg_pcd_t * pcd); ++ ++/** Call this to bind the function driver to the PCD Core. ++ * ++ * @param pcd Pointer on dwc_otg_pcd_t returned by dwc_otg_pcd_init function. ++ * @param fops The Function Driver Ops data structure containing pointers to all callbacks. ++ */ ++extern void dwc_otg_pcd_start(dwc_otg_pcd_t * pcd, ++ const struct dwc_otg_pcd_function_ops *fops); ++ ++/** Enables an endpoint for use. This function enables an endpoint in ++ * the PCD. The endpoint is described by the ep_desc which has the ++ * same format as a USB ep descriptor. The ep_handle parameter is used to refer ++ * to the endpoint from other API functions and in callbacks. Normally this ++ * should be called after a SET_CONFIGURATION/SET_INTERFACE to configure the ++ * core for that interface. ++ * ++ * Returns -DWC_E_INVALID if invalid parameters were passed. ++ * Returns -DWC_E_SHUTDOWN if any other error ocurred. ++ * Returns 0 on success. ++ * ++ * @param pcd The PCD ++ * @param ep_desc Endpoint descriptor ++ * @param ep_handle Handle on endpoint, that will be used to identify endpoint. ++ */ ++extern int dwc_otg_pcd_ep_enable(dwc_otg_pcd_t * pcd, ++ const uint8_t * ep_desc, void *ep_handle); ++ ++/** Disable the endpoint referenced by ep_handle. ++ * ++ * Returns -DWC_E_INVALID if invalid parameters were passed. ++ * Returns -DWC_E_SHUTDOWN if any other error ocurred. ++ * Returns 0 on success. */ ++extern int dwc_otg_pcd_ep_disable(dwc_otg_pcd_t * pcd, void *ep_handle); ++ ++/** Queue a data transfer request on the endpoint referenced by ep_handle. ++ * After the transfer is completes, the complete callback will be called with ++ * the request status. ++ * ++ * @param pcd The PCD ++ * @param ep_handle The handle of the endpoint ++ * @param buf The buffer for the data ++ * @param dma_buf The DMA buffer for the data ++ * @param buflen The length of the data transfer ++ * @param zero Specifies whether to send zero length last packet. ++ * @param req_handle Set this handle to any value to use to reference this ++ * request in the ep_dequeue function or from the complete callback ++ * @param atomic_alloc If driver need to perform atomic allocations ++ * for internal data structures. ++ * ++ * Returns -DWC_E_INVALID if invalid parameters were passed. ++ * Returns -DWC_E_SHUTDOWN if any other error ocurred. ++ * Returns 0 on success. */ ++extern int dwc_otg_pcd_ep_queue(dwc_otg_pcd_t * pcd, void *ep_handle, ++ uint8_t * buf, dwc_dma_t dma_buf, ++ uint32_t buflen, int zero, void *req_handle, ++ int atomic_alloc); ++ ++/** De-queue the specified data transfer that has not yet completed. ++ * ++ * Returns -DWC_E_INVALID if invalid parameters were passed. ++ * Returns -DWC_E_SHUTDOWN if any other error ocurred. ++ * Returns 0 on success. */ ++extern int dwc_otg_pcd_ep_dequeue(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle); ++ ++/** Halt (STALL) an endpoint or clear it. ++ * ++ * Returns -DWC_E_INVALID if invalid parameters were passed. ++ * Returns -DWC_E_SHUTDOWN if any other error ocurred. ++ * Returns -DWC_E_AGAIN if the STALL cannot be sent and must be tried again later ++ * Returns 0 on success. */ ++extern int dwc_otg_pcd_ep_halt(dwc_otg_pcd_t * pcd, void *ep_handle, int value); ++ ++/** This function */ ++extern int dwc_otg_pcd_ep_wedge(dwc_otg_pcd_t * pcd, void *ep_handle); ++ ++/** This function should be called on every hardware interrupt */ ++extern int32_t dwc_otg_pcd_handle_intr(dwc_otg_pcd_t * pcd); ++ ++/** This function returns current frame number */ ++extern int dwc_otg_pcd_get_frame_number(dwc_otg_pcd_t * pcd); ++ ++/** ++ * Start isochronous transfers on the endpoint referenced by ep_handle. ++ * For isochronous transfers duble buffering is used. ++ * After processing each of buffers comlete callback will be called with ++ * status for each transaction. ++ * ++ * @param pcd The PCD ++ * @param ep_handle The handle of the endpoint ++ * @param buf0 The virtual address of first data buffer ++ * @param buf1 The virtual address of second data buffer ++ * @param dma0 The DMA address of first data buffer ++ * @param dma1 The DMA address of second data buffer ++ * @param sync_frame Data pattern frame number ++ * @param dp_frame Data size for pattern frame ++ * @param data_per_frame Data size for regular frame ++ * @param start_frame Frame number to start transfers, if -1 then start transfers ASAP. ++ * @param buf_proc_intrvl Interval of ISOC Buffer processing ++ * @param req_handle Handle of ISOC request ++ * @param atomic_alloc Specefies whether to perform atomic allocation for ++ * internal data structures. ++ * ++ * Returns -DWC_E_NO_MEMORY if there is no enough memory. ++ * Returns -DWC_E_INVALID if incorrect arguments are passed to the function. ++ * Returns -DW_E_SHUTDOWN for any other error. ++ * Returns 0 on success ++ */ ++extern int dwc_otg_pcd_iso_ep_start(dwc_otg_pcd_t * pcd, void *ep_handle, ++ uint8_t * buf0, uint8_t * buf1, ++ dwc_dma_t dma0, dwc_dma_t dma1, ++ int sync_frame, int dp_frame, ++ int data_per_frame, int start_frame, ++ int buf_proc_intrvl, void *req_handle, ++ int atomic_alloc); ++ ++/** Stop ISOC transfers on endpoint referenced by ep_handle. ++ * ++ * @param pcd The PCD ++ * @param ep_handle The handle of the endpoint ++ * @param req_handle Handle of ISOC request ++ * ++ * Returns -DWC_E_INVALID if incorrect arguments are passed to the function ++ * Returns 0 on success ++ */ ++int dwc_otg_pcd_iso_ep_stop(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle); ++ ++/** Get ISOC packet status. ++ * ++ * @param pcd The PCD ++ * @param ep_handle The handle of the endpoint ++ * @param iso_req_handle Isochronoush request handle ++ * @param packet Number of packet ++ * @param status Out parameter for returning status ++ * @param actual Out parameter for returning actual length ++ * @param offset Out parameter for returning offset ++ * ++ */ ++extern void dwc_otg_pcd_get_iso_packet_params(dwc_otg_pcd_t * pcd, ++ void *ep_handle, ++ void *iso_req_handle, int packet, ++ int *status, int *actual, ++ int *offset); ++ ++/** Get ISOC packet count. ++ * ++ * @param pcd The PCD ++ * @param ep_handle The handle of the endpoint ++ * @param iso_req_handle ++ */ ++extern int dwc_otg_pcd_get_iso_packet_count(dwc_otg_pcd_t * pcd, ++ void *ep_handle, ++ void *iso_req_handle); ++ ++/** This function starts the SRP Protocol if no session is in progress. If ++ * a session is already in progress, but the device is suspended, ++ * remote wakeup signaling is started. ++ */ ++extern int dwc_otg_pcd_wakeup(dwc_otg_pcd_t * pcd); ++ ++/** This function returns 1 if LPM support is enabled, and 0 otherwise. */ ++extern int dwc_otg_pcd_is_lpm_enabled(dwc_otg_pcd_t * pcd); ++ ++/** This function returns 1 if remote wakeup is allowed and 0, otherwise. */ ++extern int dwc_otg_pcd_get_rmwkup_enable(dwc_otg_pcd_t * pcd); ++ ++/** Initiate SRP */ ++extern void dwc_otg_pcd_initiate_srp(dwc_otg_pcd_t * pcd); ++ ++/** Starts remote wakeup signaling. */ ++extern void dwc_otg_pcd_remote_wakeup(dwc_otg_pcd_t * pcd, int set); ++ ++/** This function returns whether device is dualspeed.*/ ++extern uint32_t dwc_otg_pcd_is_dualspeed(dwc_otg_pcd_t * pcd); ++ ++/** This function returns whether device is otg. */ ++extern uint32_t dwc_otg_pcd_is_otg(dwc_otg_pcd_t * pcd); ++ ++/** These functions allow to get hnp parameters */ ++extern uint32_t get_b_hnp_enable(dwc_otg_pcd_t * pcd); ++extern uint32_t get_a_hnp_support(dwc_otg_pcd_t * pcd); ++extern uint32_t get_a_alt_hnp_support(dwc_otg_pcd_t * pcd); ++ ++/** CFI specific Interface functions */ ++/** Allocate a cfi buffer */ ++extern uint8_t *cfiw_ep_alloc_buffer(dwc_otg_pcd_t * pcd, void *pep, ++ dwc_dma_t * addr, size_t buflen, ++ int flags); ++ ++/******************************************************************************/ ++ ++/** @} */ ++ ++#endif /* __DWC_PCD_IF_H__ */ ++ ++#endif /* DWC_HOST_ONLY */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_intr.c +@@ -0,0 +1,4077 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_intr.c $ ++ * $Revision: #93 $ ++ * $Date: 2009/04/02 $ ++ * $Change: 1224216 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_HOST_ONLY ++ ++#include "dwc_otg_pcd.h" ++ ++#ifdef DWC_UTE_CFI ++#include "dwc_otg_cfi.h" ++#endif ++ ++//#define PRINT_CFI_DMA_DESCS ++ ++#define DEBUG_EP0 ++ ++/** ++ * This function updates OTG. ++ */ ++static void dwc_otg_pcd_update_otg(dwc_otg_pcd_t * pcd, const unsigned reset) ++{ ++ ++ if (reset) { ++ pcd->b_hnp_enable = 0; ++ pcd->a_hnp_support = 0; ++ pcd->a_alt_hnp_support = 0; ++ } ++ ++ if (pcd->fops->hnp_changed) { ++ pcd->fops->hnp_changed(pcd); ++ } ++} ++ ++/** @file ++ * This file contains the implementation of the PCD Interrupt handlers. ++ * ++ * The PCD handles the device interrupts. Many conditions can cause a ++ * device interrupt. When an interrupt occurs, the device interrupt ++ * service routine determines the cause of the interrupt and ++ * dispatches handling to the appropriate function. These interrupt ++ * handling functions are described below. ++ * All interrupt registers are processed from LSB to MSB. ++ */ ++ ++/** ++ * This function prints the ep0 state for debug purposes. ++ */ ++static inline void print_ep0_state(dwc_otg_pcd_t * pcd) ++{ ++#ifdef DEBUG ++ char str[40]; ++ ++ switch (pcd->ep0state) { ++ case EP0_DISCONNECT: ++ dwc_strcpy(str, "EP0_DISCONNECT"); ++ break; ++ case EP0_IDLE: ++ dwc_strcpy(str, "EP0_IDLE"); ++ break; ++ case EP0_IN_DATA_PHASE: ++ dwc_strcpy(str, "EP0_IN_DATA_PHASE"); ++ break; ++ case EP0_OUT_DATA_PHASE: ++ dwc_strcpy(str, "EP0_OUT_DATA_PHASE"); ++ break; ++ case EP0_IN_STATUS_PHASE: ++ dwc_strcpy(str, "EP0_IN_STATUS_PHASE"); ++ break; ++ case EP0_OUT_STATUS_PHASE: ++ dwc_strcpy(str, "EP0_OUT_STATUS_PHASE"); ++ break; ++ case EP0_STALL: ++ dwc_strcpy(str, "EP0_STALL"); ++ break; ++ default: ++ dwc_strcpy(str, "EP0_INVALID"); ++ } ++ ++ DWC_DEBUGPL(DBG_ANY, "%s(%d)\n", str, pcd->ep0state); ++#endif ++} ++ ++#ifdef DWC_UTE_CFI ++static inline void print_desc(struct dwc_otg_dma_desc *ddesc, ++ const uint8_t * epname, int descnum) ++{ ++ CFI_INFO ++ ("%s DMA_DESC(%d) buf=0x%08x bytes=0x%04x; sp=0x%x; l=0x%x; sts=0x%02x; bs=0x%02x\n", ++ epname, descnum, ddesc->buf, ddesc->status.b.bytes, ++ ddesc->status.b.sp, ddesc->status.b.l, ddesc->status.b.sts, ++ ddesc->status.b.bs); ++} ++#endif ++ ++/** ++ * This function returns pointer to in ep struct with number ep_num ++ */ ++static inline dwc_otg_pcd_ep_t *get_in_ep(dwc_otg_pcd_t * pcd, uint32_t ep_num) ++{ ++ int i; ++ int num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps; ++ if (ep_num == 0) { ++ return &pcd->ep0; ++ } else { ++ for (i = 0; i < num_in_eps; ++i) { ++ if (pcd->in_ep[i].dwc_ep.num == ep_num) ++ return &pcd->in_ep[i]; ++ } ++ return 0; ++ } ++} ++ ++/** ++ * This function returns pointer to out ep struct with number ep_num ++ */ ++static inline dwc_otg_pcd_ep_t *get_out_ep(dwc_otg_pcd_t * pcd, uint32_t ep_num) ++{ ++ int i; ++ int num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps; ++ if (ep_num == 0) { ++ return &pcd->ep0; ++ } else { ++ for (i = 0; i < num_out_eps; ++i) { ++ if (pcd->out_ep[i].dwc_ep.num == ep_num) ++ return &pcd->out_ep[i]; ++ } ++ return 0; ++ } ++} ++ ++/** ++ * This functions gets a pointer to an EP from the wIndex address ++ * value of the control request. ++ */ ++dwc_otg_pcd_ep_t *get_ep_by_addr(dwc_otg_pcd_t * pcd, u16 wIndex) ++{ ++ dwc_otg_pcd_ep_t *ep; ++ uint32_t ep_num = UE_GET_ADDR(wIndex); ++ ++ if (ep_num == 0) { ++ ep = &pcd->ep0; ++ } else if (UE_GET_DIR(wIndex) == UE_DIR_IN) { /* in ep */ ++ ep = &pcd->in_ep[ep_num - 1]; ++ } else { ++ ep = &pcd->out_ep[ep_num - 1]; ++ } ++ ++ return ep; ++} ++ ++/** ++ * This function checks the EP request queue, if the queue is not ++ * empty the next request is started. ++ */ ++void start_next_request(dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_otg_pcd_request_t *req = 0; ++ uint32_t max_transfer = ++ GET_CORE_IF(ep->pcd)->core_params->max_transfer_size; ++ ++#ifdef DWC_UTE_CFI ++ struct dwc_otg_pcd *pcd; ++ pcd = ep->pcd; ++#endif ++ ++ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ ++#ifdef DWC_UTE_CFI ++ if (ep->dwc_ep.buff_mode != BM_STANDARD) { ++ ep->dwc_ep.cfi_req_len = req->length; ++ pcd->cfi->ops.build_descriptors(pcd->cfi, pcd, ep, req); ++ } else { ++#endif ++ /* Setup and start the Transfer */ ++ ep->dwc_ep.dma_addr = req->dma; ++ ep->dwc_ep.start_xfer_buff = req->buf; ++ ep->dwc_ep.xfer_buff = req->buf; ++ ep->dwc_ep.sent_zlp = 0; ++ ep->dwc_ep.total_len = req->length; ++ ep->dwc_ep.xfer_len = 0; ++ ep->dwc_ep.xfer_count = 0; ++ ++ ep->dwc_ep.maxxfer = max_transfer; ++ if (GET_CORE_IF(ep->pcd)->dma_desc_enable) { ++ uint32_t out_max_xfer = DDMA_MAX_TRANSFER_SIZE ++ - (DDMA_MAX_TRANSFER_SIZE % 4); ++ if (ep->dwc_ep.is_in) { ++ if (ep->dwc_ep.maxxfer > ++ DDMA_MAX_TRANSFER_SIZE) { ++ ep->dwc_ep.maxxfer = ++ DDMA_MAX_TRANSFER_SIZE; ++ } ++ } else { ++ if (ep->dwc_ep.maxxfer > out_max_xfer) { ++ ep->dwc_ep.maxxfer = ++ out_max_xfer; ++ } ++ } ++ } ++ if (ep->dwc_ep.maxxfer < ep->dwc_ep.total_len) { ++ ep->dwc_ep.maxxfer -= ++ (ep->dwc_ep.maxxfer % ep->dwc_ep.maxpacket); ++ } ++ if (req->sent_zlp) { ++ if ((ep->dwc_ep.total_len % ++ ep->dwc_ep.maxpacket == 0) ++ && (ep->dwc_ep.total_len != 0)) { ++ ep->dwc_ep.sent_zlp = 1; ++ } ++ ++ } ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ dwc_otg_ep_start_transfer(GET_CORE_IF(ep->pcd), &ep->dwc_ep); ++ } ++} ++ ++/** ++ * This function handles the SOF Interrupts. At this time the SOF ++ * Interrupt is disabled. ++ */ ++int32_t dwc_otg_pcd_handle_sof_intr(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ ++ gintsts_data_t gintsts; ++ ++ DWC_DEBUGPL(DBG_PCD, "SOF\n"); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.sofintr = 1; ++ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This function handles the Rx Status Queue Level Interrupt, which ++ * indicates that there is a least one packet in the Rx FIFO. The ++ * packets are moved from the FIFO to memory, where they will be ++ * processed when the Endpoint Interrupt Register indicates Transfer ++ * Complete or SETUP Phase Done. ++ * ++ * Repeat the following until the Rx Status Queue is empty: ++ * -# Read the Receive Status Pop Register (GRXSTSP) to get Packet ++ * info ++ * -# If Receive FIFO is empty then skip to step Clear the interrupt ++ * and exit ++ * -# If SETUP Packet call dwc_otg_read_setup_packet to copy the ++ * SETUP data to the buffer ++ * -# If OUT Data Packet call dwc_otg_read_packet to copy the data ++ * to the destination buffer ++ */ ++int32_t dwc_otg_pcd_handle_rx_status_q_level_intr(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ gintmsk_data_t gintmask = {.d32 = 0 }; ++ device_grxsts_data_t status; ++ dwc_otg_pcd_ep_t *ep; ++ gintsts_data_t gintsts; ++#ifdef DEBUG ++ static char *dpid_str[] = { "D0", "D2", "D1", "MDATA" }; ++#endif ++ ++ //DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, _pcd); ++ /* Disable the Rx Status Queue Level interrupt */ ++ gintmask.b.rxstsqlvl = 1; ++ dwc_modify_reg32(&global_regs->gintmsk, gintmask.d32, 0); ++ ++ /* Get the Status from the top of the FIFO */ ++ status.d32 = dwc_read_reg32(&global_regs->grxstsp); ++ ++ DWC_DEBUGPL(DBG_PCD, "EP:%d BCnt:%d DPID:%s " ++ "pktsts:%x Frame:%d(0x%0x)\n", ++ status.b.epnum, status.b.bcnt, ++ dpid_str[status.b.dpid], ++ status.b.pktsts, status.b.fn, status.b.fn); ++ /* Get pointer to EP structure */ ++ ep = get_out_ep(pcd, status.b.epnum); ++ ++ switch (status.b.pktsts) { ++ case DWC_DSTS_GOUT_NAK: ++ DWC_DEBUGPL(DBG_PCDV, "Global OUT NAK\n"); ++ break; ++ case DWC_STS_DATA_UPDT: ++ DWC_DEBUGPL(DBG_PCDV, "OUT Data Packet\n"); ++ if (status.b.bcnt && ep->dwc_ep.xfer_buff) { ++ /** @todo NGS Check for buffer overflow? */ ++ dwc_otg_read_packet(core_if, ++ ep->dwc_ep.xfer_buff, ++ status.b.bcnt); ++ ep->dwc_ep.xfer_count += status.b.bcnt; ++ ep->dwc_ep.xfer_buff += status.b.bcnt; ++ } ++ break; ++ case DWC_STS_XFER_COMP: ++ DWC_DEBUGPL(DBG_PCDV, "OUT Complete\n"); ++ break; ++ case DWC_DSTS_SETUP_COMP: ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "Setup Complete\n"); ++#endif ++ break; ++ case DWC_DSTS_SETUP_UPDT: ++ dwc_otg_read_setup_packet(core_if, pcd->setup_pkt->d32); ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, ++ "SETUP PKT: %02x.%02x v%04x i%04x l%04x\n", ++ pcd->setup_pkt->req.bmRequestType, ++ pcd->setup_pkt->req.bRequest, ++ UGETW(pcd->setup_pkt->req.wValue), ++ UGETW(pcd->setup_pkt->req.wIndex), ++ UGETW(pcd->setup_pkt->req.wLength)); ++#endif ++ ep->dwc_ep.xfer_count += status.b.bcnt; ++ break; ++ default: ++ DWC_DEBUGPL(DBG_PCDV, "Invalid Packet Status (0x%0x)\n", ++ status.b.pktsts); ++ break; ++ } ++ ++ /* Enable the Rx Status Queue Level interrupt */ ++ dwc_modify_reg32(&global_regs->gintmsk, 0, gintmask.d32); ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.rxstsqlvl = 1; ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ //DWC_DEBUGPL(DBG_PCDV, "EXIT: %s\n", __func__); ++ return 1; ++} ++ ++/** ++ * This function examines the Device IN Token Learning Queue to ++ * determine the EP number of the last IN token received. This ++ * implementation is for the Mass Storage device where there are only ++ * 2 IN EPs (Control-IN and BULK-IN). ++ * ++ * The EP numbers for the first six IN Tokens are in DTKNQR1 and there ++ * are 8 EP Numbers in each of the other possible DTKNQ Registers. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * ++ */ ++static inline int get_ep_of_last_in_token(dwc_otg_core_if_t * core_if) ++{ ++ dwc_otg_device_global_regs_t *dev_global_regs = ++ core_if->dev_if->dev_global_regs; ++ const uint32_t TOKEN_Q_DEPTH = core_if->hwcfg2.b.dev_token_q_depth; ++ /* Number of Token Queue Registers */ ++ const int DTKNQ_REG_CNT = (TOKEN_Q_DEPTH + 7) / 8; ++ dtknq1_data_t dtknqr1; ++ uint32_t in_tkn_epnums[4]; ++ int ndx = 0; ++ int i = 0; ++ volatile uint32_t *addr = &dev_global_regs->dtknqr1; ++ int epnum = 0; ++ ++ //DWC_DEBUGPL(DBG_PCD,"dev_token_q_depth=%d\n",TOKEN_Q_DEPTH); ++ ++ /* Read the DTKNQ Registers */ ++ for (i = 0; i < DTKNQ_REG_CNT; i++) { ++ in_tkn_epnums[i] = dwc_read_reg32(addr); ++ DWC_DEBUGPL(DBG_PCDV, "DTKNQR%d=0x%08x\n", i + 1, ++ in_tkn_epnums[i]); ++ if (addr == &dev_global_regs->dvbusdis) { ++ addr = &dev_global_regs->dtknqr3_dthrctl; ++ } else { ++ ++addr; ++ } ++ ++ } ++ ++ /* Copy the DTKNQR1 data to the bit field. */ ++ dtknqr1.d32 = in_tkn_epnums[0]; ++ /* Get the EP numbers */ ++ in_tkn_epnums[0] = dtknqr1.b.epnums0_5; ++ ndx = dtknqr1.b.intknwptr - 1; ++ ++ //DWC_DEBUGPL(DBG_PCDV,"ndx=%d\n",ndx); ++ if (ndx == -1) { ++ /** @todo Find a simpler way to calculate the max ++ * queue position.*/ ++ int cnt = TOKEN_Q_DEPTH; ++ if (TOKEN_Q_DEPTH <= 6) { ++ cnt = TOKEN_Q_DEPTH - 1; ++ } else if (TOKEN_Q_DEPTH <= 14) { ++ cnt = TOKEN_Q_DEPTH - 7; ++ } else if (TOKEN_Q_DEPTH <= 22) { ++ cnt = TOKEN_Q_DEPTH - 15; ++ } else { ++ cnt = TOKEN_Q_DEPTH - 23; ++ } ++ epnum = (in_tkn_epnums[DTKNQ_REG_CNT - 1] >> (cnt * 4)) & 0xF; ++ } else { ++ if (ndx <= 5) { ++ epnum = (in_tkn_epnums[0] >> (ndx * 4)) & 0xF; ++ } else if (ndx <= 13) { ++ ndx -= 6; ++ epnum = (in_tkn_epnums[1] >> (ndx * 4)) & 0xF; ++ } else if (ndx <= 21) { ++ ndx -= 14; ++ epnum = (in_tkn_epnums[2] >> (ndx * 4)) & 0xF; ++ } else if (ndx <= 29) { ++ ndx -= 22; ++ epnum = (in_tkn_epnums[3] >> (ndx * 4)) & 0xF; ++ } ++ } ++ //DWC_DEBUGPL(DBG_PCD,"epnum=%d\n",epnum); ++ return epnum; ++} ++ ++/** ++ * This interrupt occurs when the non-periodic Tx FIFO is half-empty. ++ * The active request is checked for the next packet to be loaded into ++ * the non-periodic Tx FIFO. ++ */ ++int32_t dwc_otg_pcd_handle_np_tx_fifo_empty_intr(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ dwc_otg_dev_in_ep_regs_t *ep_regs; ++ gnptxsts_data_t txstatus = {.d32 = 0 }; ++ gintsts_data_t gintsts; ++ ++ int epnum = 0; ++ dwc_otg_pcd_ep_t *ep = 0; ++ uint32_t len = 0; ++ int dwords; ++ ++ /* Get the epnum from the IN Token Learning Queue. */ ++ epnum = get_ep_of_last_in_token(core_if); ++ ep = get_in_ep(pcd, epnum); ++ ++ DWC_DEBUGPL(DBG_PCD, "NP TxFifo Empty: %d \n", epnum); ++ ++ ep_regs = core_if->dev_if->in_ep_regs[epnum]; ++ ++ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; ++ if (len > ep->dwc_ep.maxpacket) { ++ len = ep->dwc_ep.maxpacket; ++ } ++ dwords = (len + 3) / 4; ++ ++ /* While there is space in the queue and space in the FIFO and ++ * More data to tranfer, Write packets to the Tx FIFO */ ++ txstatus.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_PCDV, "b4 GNPTXSTS=0x%08x\n", txstatus.d32); ++ ++ while (txstatus.b.nptxqspcavail > 0 && ++ txstatus.b.nptxfspcavail > dwords && ++ ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len) { ++ /* Write the FIFO */ ++ dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0); ++ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; ++ ++ if (len > ep->dwc_ep.maxpacket) { ++ len = ep->dwc_ep.maxpacket; ++ } ++ ++ dwords = (len + 3) / 4; ++ txstatus.d32 = dwc_read_reg32(&global_regs->gnptxsts); ++ DWC_DEBUGPL(DBG_PCDV, "GNPTXSTS=0x%08x\n", txstatus.d32); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "GNPTXSTS=0x%08x\n", ++ dwc_read_reg32(&global_regs->gnptxsts)); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.nptxfempty = 1; ++ dwc_write_reg32(&global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This function is called when dedicated Tx FIFO Empty interrupt occurs. ++ * The active request is checked for the next packet to be loaded into ++ * apropriate Tx FIFO. ++ */ ++static int32_t write_empty_tx_fifo(dwc_otg_pcd_t * pcd, uint32_t epnum) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dwc_otg_dev_in_ep_regs_t *ep_regs; ++ dtxfsts_data_t txstatus = {.d32 = 0 }; ++ dwc_otg_pcd_ep_t *ep = 0; ++ uint32_t len = 0; ++ int dwords; ++ ++ ep = get_in_ep(pcd, epnum); ++ ++ DWC_DEBUGPL(DBG_PCD, "Dedicated TxFifo Empty: %d \n", epnum); ++ ++ ep_regs = core_if->dev_if->in_ep_regs[epnum]; ++ ++ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; ++ ++ if (len > ep->dwc_ep.maxpacket) { ++ len = ep->dwc_ep.maxpacket; ++ } ++ ++ dwords = (len + 3) / 4; ++ ++ /* While there is space in the queue and space in the FIFO and ++ * More data to tranfer, Write packets to the Tx FIFO */ ++ txstatus.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, txstatus.d32); ++ ++ while (txstatus.b.txfspcavail > dwords && ++ ep->dwc_ep.xfer_count < ep->dwc_ep.xfer_len && ++ ep->dwc_ep.xfer_len != 0) { ++ /* Write the FIFO */ ++ dwc_otg_ep_write_packet(core_if, &ep->dwc_ep, 0); ++ ++ len = ep->dwc_ep.xfer_len - ep->dwc_ep.xfer_count; ++ if (len > ep->dwc_ep.maxpacket) { ++ len = ep->dwc_ep.maxpacket; ++ } ++ ++ dwords = (len + 3) / 4; ++ txstatus.d32 = ++ dwc_read_reg32(&dev_if->in_ep_regs[epnum]->dtxfsts); ++ DWC_DEBUGPL(DBG_PCDV, "dtxfsts[%d]=0x%08x\n", epnum, ++ txstatus.d32); ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, "b4 dtxfsts[%d]=0x%08x\n", epnum, ++ dwc_read_reg32(&dev_if->in_ep_regs[epnum]->dtxfsts)); ++ ++ return 1; ++} ++ ++/** ++ * This function is called when the Device is disconnected. It stops ++ * any active requests and informs the Gadget driver of the ++ * disconnect. ++ */ ++void dwc_otg_pcd_stop(dwc_otg_pcd_t * pcd) ++{ ++ int i, num_in_eps, num_out_eps; ++ dwc_otg_pcd_ep_t *ep; ++ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_SPINLOCK(pcd->lock); ++ ++ num_in_eps = GET_CORE_IF(pcd)->dev_if->num_in_eps; ++ num_out_eps = GET_CORE_IF(pcd)->dev_if->num_out_eps; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s() \n", __func__); ++ /* don't disconnect drivers more than once */ ++ if (pcd->ep0state == EP0_DISCONNECT) { ++ DWC_DEBUGPL(DBG_ANY, "%s() Already Disconnected\n", __func__); ++ return; ++ } ++ pcd->ep0state = EP0_DISCONNECT; ++ ++ /* Reset the OTG state. */ ++ dwc_otg_pcd_update_otg(pcd, 1); ++ ++ /* Disable the NP Tx Fifo Empty Interrupt. */ ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Flush the FIFOs */ ++ /**@todo NGS Flush Periodic FIFOs */ ++ dwc_otg_flush_tx_fifo(GET_CORE_IF(pcd), 0x10); ++ dwc_otg_flush_rx_fifo(GET_CORE_IF(pcd)); ++ ++ /* prevent new request submissions, kill any outstanding requests */ ++ ep = &pcd->ep0; ++ dwc_otg_request_nuke(ep); ++ /* prevent new request submissions, kill any outstanding requests */ ++ for (i = 0; i < num_in_eps; i++) { ++ dwc_otg_pcd_ep_t *ep = &pcd->in_ep[i]; ++ dwc_otg_request_nuke(ep); ++ } ++ /* prevent new request submissions, kill any outstanding requests */ ++ for (i = 0; i < num_out_eps; i++) { ++ dwc_otg_pcd_ep_t *ep = &pcd->out_ep[i]; ++ dwc_otg_request_nuke(ep); ++ } ++ ++ /* report disconnect; the driver is already quiesced */ ++ if (pcd->fops->disconnect) { ++ DWC_SPINUNLOCK(pcd->lock); ++ pcd->fops->disconnect(pcd); ++ DWC_SPINLOCK(pcd->lock); ++ } ++ DWC_SPINUNLOCK(pcd->lock); ++} ++ ++/** ++ * This interrupt indicates that ... ++ */ ++int32_t dwc_otg_pcd_handle_i2c_intr(dwc_otg_pcd_t * pcd) ++{ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ gintsts_data_t gintsts; ++ ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "i2cintr"); ++ intr_mask.b.i2cintr = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.i2cintr = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that ... ++ */ ++int32_t dwc_otg_pcd_handle_early_suspend_intr(dwc_otg_pcd_t * pcd) ++{ ++ gintsts_data_t gintsts; ++#if defined(VERBOSE) ++ DWC_PRINTF("Early Suspend Detected\n"); ++#endif ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.erlysuspend = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ return 1; ++} ++ ++/** ++ * This function configures EPO to receive SETUP packets. ++ * ++ * @todo NGS: Update the comments from the HW FS. ++ * ++ * -# Program the following fields in the endpoint specific registers ++ * for Control OUT EP 0, in order to receive a setup packet ++ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back ++ * setup packets) ++ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back ++ * to back setup packets) ++ * - In DMA mode, DOEPDMA0 Register with a memory address to ++ * store any setup packets received ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param pcd Programming view of the PCD. ++ */ ++static inline void ep0_out_start(dwc_otg_core_if_t * core_if, ++ dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ deptsiz0_data_t doeptsize0 = {.d32 = 0 }; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ depctl_data_t doepctl = {.d32 = 0 }; ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, "%s() doepctl0=%0x\n", __func__, ++ dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl)); ++#endif ++ ++ doeptsize0.b.supcnt = 3; ++ doeptsize0.b.pktcnt = 1; ++ doeptsize0.b.xfersize = 8 * 3; ++ ++ if (core_if->dma_enable) { ++ if (!core_if->dma_desc_enable) { ++ /** put here as for Hermes mode deptisz register should not be written */ ++ dwc_write_reg32(&dev_if->out_ep_regs[0]->doeptsiz, ++ doeptsize0.d32); ++ ++ /** @todo dma needs to handle multiple setup packets (up to 3) */ ++ dwc_write_reg32(&dev_if->out_ep_regs[0]->doepdma, ++ pcd->setup_pkt_dma_handle); ++ } else { ++ dev_if->setup_desc_index = ++ (dev_if->setup_desc_index + 1) & 1; ++ dma_desc = ++ dev_if->setup_desc_addr[dev_if->setup_desc_index]; ++ ++ /** DMA Descriptor Setup */ ++ dma_desc->status.b.bs = BS_HOST_BUSY; ++ dma_desc->status.b.l = 1; ++ dma_desc->status.b.ioc = 1; ++ dma_desc->status.b.bytes = pcd->ep0.dwc_ep.maxpacket; ++ dma_desc->buf = pcd->setup_pkt_dma_handle; ++ dma_desc->status.b.bs = BS_HOST_READY; ++ ++ /** DOEPDMA0 Register write */ ++ dwc_write_reg32(&dev_if->out_ep_regs[0]->doepdma, ++ dev_if->dma_setup_desc_addr[dev_if-> ++ setup_desc_index]); ++ } ++ ++ } else { ++ /** put here as for Hermes mode deptisz register should not be written */ ++ dwc_write_reg32(&dev_if->out_ep_regs[0]->doeptsiz, ++ doeptsize0.d32); ++ } ++ ++ /** DOEPCTL0 Register write */ ++ doepctl.b.epena = 1; ++ doepctl.b.cnak = 1; ++ dwc_write_reg32(&dev_if->out_ep_regs[0]->doepctl, doepctl.d32); ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, "doepctl0=%0x\n", ++ dwc_read_reg32(&dev_if->out_ep_regs[0]->doepctl)); ++ DWC_DEBUGPL(DBG_PCDV, "diepctl0=%0x\n", ++ dwc_read_reg32(&dev_if->in_ep_regs[0]->diepctl)); ++#endif ++} ++ ++/** ++ * This interrupt occurs when a USB Reset is detected. When the USB ++ * Reset Interrupt occurs the device state is set to DEFAULT and the ++ * EP0 state is set to IDLE. ++ * -# Set the NAK bit for all OUT endpoints (DOEPCTLn.SNAK = 1) ++ * -# Unmask the following interrupt bits ++ * - DAINTMSK.INEP0 = 1 (Control 0 IN endpoint) ++ * - DAINTMSK.OUTEP0 = 1 (Control 0 OUT endpoint) ++ * - DOEPMSK.SETUP = 1 ++ * - DOEPMSK.XferCompl = 1 ++ * - DIEPMSK.XferCompl = 1 ++ * - DIEPMSK.TimeOut = 1 ++ * -# Program the following fields in the endpoint specific registers ++ * for Control OUT EP 0, in order to receive a setup packet ++ * - DOEPTSIZ0.Packet Count = 3 (To receive up to 3 back to back ++ * setup packets) ++ * - DOEPTSIZE0.Transfer Size = 24 Bytes (To receive up to 3 back ++ * to back setup packets) ++ * - In DMA mode, DOEPDMA0 Register with a memory address to ++ * store any setup packets received ++ * At this point, all the required initialization, except for enabling ++ * the control 0 OUT endpoint is done, for receiving SETUP packets. ++ */ ++int32_t dwc_otg_pcd_handle_usb_reset_intr(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ depctl_data_t doepctl = {.d32 = 0 }; ++ daint_data_t daintmsk = {.d32 = 0 }; ++ doepmsk_data_t doepmsk = {.d32 = 0 }; ++ diepmsk_data_t diepmsk = {.d32 = 0 }; ++ dcfg_data_t dcfg = {.d32 = 0 }; ++ grstctl_t resetctl = {.d32 = 0 }; ++ dctl_data_t dctl = {.d32 = 0 }; ++ int i = 0; ++ gintsts_data_t gintsts; ++ pcgcctl_data_t power = {.d32 = 0 }; ++ ++ power.d32 = dwc_read_reg32(core_if->pcgcctl); ++ if (power.b.stoppclk) { ++ power.d32 = 0; ++ power.b.stoppclk = 1; ++ dwc_modify_reg32(core_if->pcgcctl, power.d32, 0); ++ ++ power.b.pwrclmp = 1; ++ dwc_modify_reg32(core_if->pcgcctl, power.d32, 0); ++ ++ power.b.rstpdwnmodule = 1; ++ dwc_modify_reg32(core_if->pcgcctl, power.d32, 0); ++ } ++ ++ core_if->lx_state = DWC_OTG_L0; ++ ++ DWC_PRINTF("USB RESET\n"); ++#ifdef DWC_EN_ISOC ++ for (i = 1; i < 16; ++i) { ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ ep = get_in_ep(pcd, i); ++ if (ep != 0) { ++ dwc_ep = &ep->dwc_ep; ++ dwc_ep->next_frame = 0xffffffff; ++ } ++ } ++#endif /* DWC_EN_ISOC */ ++ ++ /* reset the HNP settings */ ++ dwc_otg_pcd_update_otg(pcd, 1); ++ ++ /* Clear the Remote Wakeup Signalling */ ++ dctl.b.rmtwkupsig = 1; ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32, 0); ++ ++ /* Set NAK for all OUT EPs */ ++ doepctl.b.snak = 1; ++ for (i = 0; i <= dev_if->num_out_eps; i++) { ++ dwc_write_reg32(&dev_if->out_ep_regs[i]->doepctl, doepctl.d32); ++ } ++ ++ /* Flush the NP Tx FIFO */ ++ dwc_otg_flush_tx_fifo(core_if, 0x10); ++ /* Flush the Learning Queue */ ++ resetctl.b.intknqflsh = 1; ++ dwc_write_reg32(&core_if->core_global_regs->grstctl, resetctl.d32); ++ ++ if (core_if->multiproc_int_enable) { ++ daintmsk.b.inep0 = 1; ++ daintmsk.b.outep0 = 1; ++ dwc_write_reg32(&dev_if->dev_global_regs->deachintmsk, ++ daintmsk.d32); ++ ++ doepmsk.b.setup = 1; ++ doepmsk.b.xfercompl = 1; ++ doepmsk.b.ahberr = 1; ++ doepmsk.b.epdisabled = 1; ++ ++ if (core_if->dma_desc_enable) { ++ doepmsk.b.stsphsercvd = 1; ++ doepmsk.b.bna = 1; ++ } ++/* ++ doepmsk.b.babble = 1; ++ doepmsk.b.nyet = 1; ++ ++ if(core_if->dma_enable) { ++ doepmsk.b.nak = 1; ++ } ++*/ ++ dwc_write_reg32(&dev_if->dev_global_regs->doepeachintmsk[0], ++ doepmsk.d32); ++ ++ diepmsk.b.xfercompl = 1; ++ diepmsk.b.timeout = 1; ++ diepmsk.b.epdisabled = 1; ++ diepmsk.b.ahberr = 1; ++ diepmsk.b.intknepmis = 1; ++ ++ if (core_if->dma_desc_enable) { ++ diepmsk.b.bna = 1; ++ } ++/* ++ if(core_if->dma_enable) { ++ diepmsk.b.nak = 1; ++ } ++*/ ++ dwc_write_reg32(&dev_if->dev_global_regs->diepeachintmsk[0], ++ diepmsk.d32); ++ } else { ++ daintmsk.b.inep0 = 1; ++ daintmsk.b.outep0 = 1; ++ dwc_write_reg32(&dev_if->dev_global_regs->daintmsk, ++ daintmsk.d32); ++ ++ doepmsk.b.setup = 1; ++ doepmsk.b.xfercompl = 1; ++ doepmsk.b.ahberr = 1; ++ doepmsk.b.epdisabled = 1; ++ ++ if (core_if->dma_desc_enable) { ++ doepmsk.b.stsphsercvd = 1; ++ doepmsk.b.bna = 1; ++ } ++ dwc_write_reg32(&dev_if->dev_global_regs->doepmsk, doepmsk.d32); ++ ++ diepmsk.b.xfercompl = 1; ++ diepmsk.b.timeout = 1; ++ diepmsk.b.epdisabled = 1; ++ diepmsk.b.ahberr = 1; ++ diepmsk.b.intknepmis = 1; ++ ++ if (core_if->dma_desc_enable) { ++ diepmsk.b.bna = 1; ++ } ++ ++ dwc_write_reg32(&dev_if->dev_global_regs->diepmsk, diepmsk.d32); ++ } ++ ++ /* Reset Device Address */ ++ dcfg.d32 = dwc_read_reg32(&dev_if->dev_global_regs->dcfg); ++ dcfg.b.devaddr = 0; ++ dwc_write_reg32(&dev_if->dev_global_regs->dcfg, dcfg.d32); ++ ++ /* setup EP0 to receive SETUP packets */ ++ ep0_out_start(core_if, pcd); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.usbreset = 1; ++ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * Get the device speed from the device status register and convert it ++ * to USB speed constant. ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ */ ++static int get_device_speed(dwc_otg_core_if_t * core_if) ++{ ++ dsts_data_t dsts; ++ int speed = 0; ++ dsts.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dsts); ++ ++ switch (dsts.b.enumspd) { ++ case DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ: ++ speed = USB_SPEED_HIGH; ++ break; ++ case DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ: ++ case DWC_DSTS_ENUMSPD_FS_PHY_48MHZ: ++ speed = USB_SPEED_FULL; ++ break; ++ ++ case DWC_DSTS_ENUMSPD_LS_PHY_6MHZ: ++ speed = USB_SPEED_LOW; ++ break; ++ } ++ ++ return speed; ++} ++ ++/** ++ * Read the device status register and set the device speed in the ++ * data structure. ++ * Set up EP0 to receive SETUP packets by calling dwc_ep0_activate. ++ */ ++int32_t dwc_otg_pcd_handle_enum_done_intr(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ gintsts_data_t gintsts; ++ gusbcfg_data_t gusbcfg; ++ dwc_otg_core_global_regs_t *global_regs = ++ GET_CORE_IF(pcd)->core_global_regs; ++ uint8_t utmi16b, utmi8b; ++ int speed; ++ DWC_DEBUGPL(DBG_PCD, "SPEED ENUM\n"); ++ ++ if (GET_CORE_IF(pcd)->snpsid >= OTG_CORE_REV_2_60a) { ++ utmi16b = 6; ++ utmi8b = 9; ++ } else { ++ utmi16b = 4; ++ utmi8b = 8; ++ } ++ dwc_otg_ep0_activate(GET_CORE_IF(pcd), &ep0->dwc_ep); ++ ++#ifdef DEBUG_EP0 ++ print_ep0_state(pcd); ++#endif ++ ++ if (pcd->ep0state == EP0_DISCONNECT) { ++ pcd->ep0state = EP0_IDLE; ++ } else if (pcd->ep0state == EP0_STALL) { ++ pcd->ep0state = EP0_IDLE; ++ } ++ ++ pcd->ep0state = EP0_IDLE; ++ ++ ep0->stopped = 0; ++ ++ speed = get_device_speed(GET_CORE_IF(pcd)); ++ pcd->fops->connect(pcd, speed); ++ ++ /* Set USB turnaround time based on device speed and PHY interface. */ ++ gusbcfg.d32 = dwc_read_reg32(&global_regs->gusbcfg); ++ if (speed == USB_SPEED_HIGH) { ++ if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == ++ DWC_HWCFG2_HS_PHY_TYPE_ULPI) { ++ /* ULPI interface */ ++ gusbcfg.b.usbtrdtim = 9; ++ } ++ if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == ++ DWC_HWCFG2_HS_PHY_TYPE_UTMI) { ++ /* UTMI+ interface */ ++ if (GET_CORE_IF(pcd)->hwcfg4.b.utmi_phy_data_width == 0) { ++ gusbcfg.b.usbtrdtim = utmi8b; ++ } else if (GET_CORE_IF(pcd)->hwcfg4.b. ++ utmi_phy_data_width == 1) { ++ gusbcfg.b.usbtrdtim = utmi16b; ++ } else if (GET_CORE_IF(pcd)->core_params-> ++ phy_utmi_width == 8) { ++ gusbcfg.b.usbtrdtim = utmi8b; ++ } else { ++ gusbcfg.b.usbtrdtim = utmi16b; ++ } ++ } ++ if (GET_CORE_IF(pcd)->hwcfg2.b.hs_phy_type == ++ DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI) { ++ /* UTMI+ OR ULPI interface */ ++ if (gusbcfg.b.ulpi_utmi_sel == 1) { ++ /* ULPI interface */ ++ gusbcfg.b.usbtrdtim = 9; ++ } else { ++ /* UTMI+ interface */ ++ if (GET_CORE_IF(pcd)->core_params-> ++ phy_utmi_width == 16) { ++ gusbcfg.b.usbtrdtim = utmi16b; ++ } else { ++ gusbcfg.b.usbtrdtim = utmi8b; ++ } ++ } ++ } ++ } else { ++ /* Full or low speed */ ++ gusbcfg.b.usbtrdtim = 9; ++ } ++ dwc_write_reg32(&global_regs->gusbcfg, gusbcfg.d32); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.enumdone = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that the ISO OUT Packet was dropped due to ++ * Rx FIFO full or Rx Status Queue Full. If this interrupt occurs ++ * read all the data from the Rx FIFO. ++ */ ++int32_t dwc_otg_pcd_handle_isoc_out_packet_dropped_intr(dwc_otg_pcd_t * pcd) ++{ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ gintsts_data_t gintsts; ++ ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", ++ "ISOC Out Dropped"); ++ ++ intr_mask.b.isooutdrop = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.isooutdrop = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates the end of the portion of the micro-frame ++ * for periodic transactions. If there is a periodic transaction for ++ * the next frame, load the packets into the EP periodic Tx FIFO. ++ */ ++int32_t dwc_otg_pcd_handle_end_periodic_frame_intr(dwc_otg_pcd_t * pcd) ++{ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ gintsts_data_t gintsts; ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "EOP"); ++ ++ intr_mask.b.eopframe = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.eopframe = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that EP of the packet on the top of the ++ * non-periodic Tx FIFO does not match EP of the IN Token received. ++ * ++ * The "Device IN Token Queue" Registers are read to determine the ++ * order the IN Tokens have been received. The non-periodic Tx FIFO ++ * is flushed, so it can be reloaded in the order seen in the IN Token ++ * Queue. ++ */ ++int32_t dwc_otg_pcd_handle_ep_mismatch_intr(dwc_otg_core_if_t * core_if) ++{ ++ gintsts_data_t gintsts; ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, core_if); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.epmismatch = 1; ++ dwc_write_reg32(&core_if->core_global_regs->gintsts, gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This funcion stalls EP0. ++ */ ++static inline void ep0_do_stall(dwc_otg_pcd_t * pcd, const int err_val) ++{ ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ usb_device_request_t *ctrl = &pcd->setup_pkt->req; ++ DWC_WARN("req %02x.%02x protocol STALL; err %d\n", ++ ctrl->bmRequestType, ctrl->bRequest, err_val); ++ ++ ep0->dwc_ep.is_in = 1; ++ dwc_otg_ep_set_stall(GET_CORE_IF(pcd), &ep0->dwc_ep); ++ pcd->ep0.stopped = 1; ++ pcd->ep0state = EP0_IDLE; ++ ep0_out_start(GET_CORE_IF(pcd), pcd); ++} ++ ++/** ++ * This functions delegates the setup command to the gadget driver. ++ */ ++static inline void do_gadget_setup(dwc_otg_pcd_t * pcd, ++ usb_device_request_t * ctrl) ++{ ++ int ret = 0; ++ DWC_SPINUNLOCK(pcd->lock); ++ ret = pcd->fops->setup(pcd, (uint8_t *) ctrl); ++ DWC_SPINLOCK(pcd->lock); ++ if (ret < 0) { ++ ep0_do_stall(pcd, ret); ++ } ++ ++ /** @todo This is a g_file_storage gadget driver specific ++ * workaround: a DELAYED_STATUS result from the fsg_setup ++ * routine will result in the gadget queueing a EP0 IN status ++ * phase for a two-stage control transfer. Exactly the same as ++ * a SET_CONFIGURATION/SET_INTERFACE except that this is a class ++ * specific request. Need a generic way to know when the gadget ++ * driver will queue the status phase. Can we assume when we ++ * call the gadget driver setup() function that it will always ++ * queue and require the following flag? Need to look into ++ * this. ++ */ ++ ++ if (ret == 256 + 999) { ++ pcd->request_config = 1; ++ } ++} ++ ++#ifdef DWC_UTE_CFI ++/** ++ * This functions delegates the CFI setup commands to the gadget driver. ++ * This function will return a negative value to indicate a failure. ++ */ ++static inline int cfi_gadget_setup(dwc_otg_pcd_t * pcd, ++ struct cfi_usb_ctrlrequest *ctrl_req) ++{ ++ int ret = 0; ++ ++ if (pcd->fops && pcd->fops->cfi_setup) { ++ DWC_SPINUNLOCK(pcd->lock); ++ ret = pcd->fops->cfi_setup(pcd, ctrl_req); ++ DWC_SPINLOCK(pcd->lock); ++ if (ret < 0) { ++ ep0_do_stall(pcd, ret); ++ return ret; ++ } ++ } ++ ++ return ret; ++} ++#endif ++ ++/** ++ * This function starts the Zero-Length Packet for the IN status phase ++ * of a 2 stage control transfer. ++ */ ++static inline void do_setup_in_status_phase(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ if (pcd->ep0state == EP0_STALL) { ++ return; ++ } ++ ++ pcd->ep0state = EP0_IN_STATUS_PHASE; ++ ++ /* Prepare for more SETUP Packets */ ++ DWC_DEBUGPL(DBG_PCD, "EP0 IN ZLP\n"); ++ ep0->dwc_ep.xfer_len = 0; ++ ep0->dwc_ep.xfer_count = 0; ++ ep0->dwc_ep.is_in = 1; ++ ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle; ++ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep); ++ ++ /* Prepare for more SETUP Packets */ ++ //ep0_out_start(GET_CORE_IF(pcd), pcd); ++} ++ ++/** ++ * This function starts the Zero-Length Packet for the OUT status phase ++ * of a 2 stage control transfer. ++ */ ++static inline void do_setup_out_status_phase(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ if (pcd->ep0state == EP0_STALL) { ++ DWC_DEBUGPL(DBG_PCD, "EP0 STALLED\n"); ++ return; ++ } ++ pcd->ep0state = EP0_OUT_STATUS_PHASE; ++ ++ DWC_DEBUGPL(DBG_PCD, "EP0 OUT ZLP\n"); ++ ep0->dwc_ep.xfer_len = 0; ++ ep0->dwc_ep.xfer_count = 0; ++ ep0->dwc_ep.is_in = 0; ++ ep0->dwc_ep.dma_addr = pcd->setup_pkt_dma_handle; ++ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep); ++ ++ /* Prepare for more SETUP Packets */ ++ if (GET_CORE_IF(pcd)->dma_enable == 0) { ++ ep0_out_start(GET_CORE_IF(pcd), pcd); ++ } ++} ++ ++/** ++ * Clear the EP halt (STALL) and if pending requests start the ++ * transfer. ++ */ ++static inline void pcd_clear_halt(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep) ++{ ++ if (ep->dwc_ep.stall_clear_flag == 0) ++ dwc_otg_ep_clear_stall(GET_CORE_IF(pcd), &ep->dwc_ep); ++ ++ /* Reactive the EP */ ++ dwc_otg_ep_activate(GET_CORE_IF(pcd), &ep->dwc_ep); ++ if (ep->stopped) { ++ ep->stopped = 0; ++ /* If there is a request in the EP queue start it */ ++ ++ /** @todo FIXME: this causes an EP mismatch in DMA mode. ++ * epmismatch not yet implemented. */ ++ ++ /* ++ * Above fixme is solved by implmenting a tasklet to call the ++ * start_next_request(), outside of interrupt context at some ++ * time after the current time, after a clear-halt setup packet. ++ * Still need to implement ep mismatch in the future if a gadget ++ * ever uses more than one endpoint at once ++ */ ++ ep->queue_sof = 1; ++ DWC_TASK_SCHEDULE(pcd->start_xfer_tasklet); ++ } ++ /* Start Control Status Phase */ ++ do_setup_in_status_phase(pcd); ++} ++ ++/** ++ * This function is called when the SET_FEATURE TEST_MODE Setup packet ++ * is sent from the host. The Device Control register is written with ++ * the Test Mode bits set to the specified Test Mode. This is done as ++ * a tasklet so that the "Status" phase of the control transfer ++ * completes before transmitting the TEST packets. ++ * ++ * @todo This has not been tested since the tasklet struct was put ++ * into the PCD struct! ++ * ++ */ ++void do_test_mode(void *data) ++{ ++ dctl_data_t dctl; ++ dwc_otg_pcd_t *pcd = (dwc_otg_pcd_t *) data; ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ int test_mode = pcd->test_mode; ++ ++// DWC_WARN("%s() has not been tested since being rewritten!\n", __func__); ++ ++ dctl.d32 = dwc_read_reg32(&core_if->dev_if->dev_global_regs->dctl); ++ switch (test_mode) { ++ case 1: // TEST_J ++ dctl.b.tstctl = 1; ++ break; ++ ++ case 2: // TEST_K ++ dctl.b.tstctl = 2; ++ break; ++ ++ case 3: // TEST_SE0_NAK ++ dctl.b.tstctl = 3; ++ break; ++ ++ case 4: // TEST_PACKET ++ dctl.b.tstctl = 4; ++ break; ++ ++ case 5: // TEST_FORCE_ENABLE ++ dctl.b.tstctl = 5; ++ break; ++ } ++ dwc_write_reg32(&core_if->dev_if->dev_global_regs->dctl, dctl.d32); ++} ++ ++/** ++ * This function process the GET_STATUS Setup Commands. ++ */ ++static inline void do_get_status(dwc_otg_pcd_t * pcd) ++{ ++ usb_device_request_t ctrl = pcd->setup_pkt->req; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ uint16_t *status = pcd->status_buf; ++ ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, ++ "GET_STATUS %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bmRequestType, ctrl.bRequest, ++ UGETW(ctrl.wValue), UGETW(ctrl.wIndex), ++ UGETW(ctrl.wLength)); ++#endif ++ ++ switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) { ++ case UT_DEVICE: ++ *status = 0x1; /* Self powered */ ++ *status |= pcd->remote_wakeup_enable << 1; ++ break; ++ ++ case UT_INTERFACE: ++ *status = 0; ++ break; ++ ++ case UT_ENDPOINT: ++ ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex)); ++ if (ep == 0 || UGETW(ctrl.wLength) > 2) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ /** @todo check for EP stall */ ++ *status = ep->stopped; ++ break; ++ } ++ pcd->ep0_pending = 1; ++ ep0->dwc_ep.start_xfer_buff = (uint8_t *) status; ++ ep0->dwc_ep.xfer_buff = (uint8_t *) status; ++ ep0->dwc_ep.dma_addr = pcd->status_buf_dma_handle; ++ ep0->dwc_ep.xfer_len = 2; ++ ep0->dwc_ep.xfer_count = 0; ++ ep0->dwc_ep.total_len = ep0->dwc_ep.xfer_len; ++ dwc_otg_ep0_start_transfer(GET_CORE_IF(pcd), &ep0->dwc_ep); ++} ++ ++/** ++ * This function process the SET_FEATURE Setup Commands. ++ */ ++static inline void do_set_feature(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++ usb_device_request_t ctrl = pcd->setup_pkt->req; ++ dwc_otg_pcd_ep_t *ep = 0; ++ int32_t otg_cap_param = core_if->core_params->otg_cap; ++ gotgctl_data_t gotgctl = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_PCD, "SET_FEATURE:%02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bmRequestType, ctrl.bRequest, ++ UGETW(ctrl.wValue), UGETW(ctrl.wIndex), ++ UGETW(ctrl.wLength)); ++ DWC_DEBUGPL(DBG_PCD, "otg_cap=%d\n", otg_cap_param); ++ ++ switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) { ++ case UT_DEVICE: ++ switch (UGETW(ctrl.wValue)) { ++ case UF_DEVICE_REMOTE_WAKEUP: ++ pcd->remote_wakeup_enable = 1; ++ break; ++ ++ case UF_TEST_MODE: ++ /* Setup the Test Mode tasklet to do the Test ++ * Packet generation after the SETUP Status ++ * phase has completed. */ ++ ++ /** @todo This has not been tested since the ++ * tasklet struct was put into the PCD ++ * struct! */ ++ pcd->test_mode = UGETW(ctrl.wIndex) >> 8; ++ DWC_TASK_SCHEDULE(pcd->test_mode_tasklet); ++ break; ++ ++ case UF_DEVICE_B_HNP_ENABLE: ++ DWC_DEBUGPL(DBG_PCDV, ++ "SET_FEATURE: USB_DEVICE_B_HNP_ENABLE\n"); ++ ++ /* dev may initiate HNP */ ++ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { ++ pcd->b_hnp_enable = 1; ++ dwc_otg_pcd_update_otg(pcd, 0); ++ DWC_DEBUGPL(DBG_PCD, "Request B HNP\n"); ++ /**@todo Is the gotgctl.devhnpen cleared ++ * by a USB Reset? */ ++ gotgctl.b.devhnpen = 1; ++ gotgctl.b.hnpreq = 1; ++ dwc_write_reg32(&global_regs->gotgctl, ++ gotgctl.d32); ++ } else { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ } ++ break; ++ ++ case UF_DEVICE_A_HNP_SUPPORT: ++ /* RH port supports HNP */ ++ DWC_DEBUGPL(DBG_PCDV, ++ "SET_FEATURE: USB_DEVICE_A_HNP_SUPPORT\n"); ++ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { ++ pcd->a_hnp_support = 1; ++ dwc_otg_pcd_update_otg(pcd, 0); ++ } else { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ } ++ break; ++ ++ case UF_DEVICE_A_ALT_HNP_SUPPORT: ++ /* other RH port does */ ++ DWC_DEBUGPL(DBG_PCDV, ++ "SET_FEATURE: USB_DEVICE_A_ALT_HNP_SUPPORT\n"); ++ if (otg_cap_param == DWC_OTG_CAP_PARAM_HNP_SRP_CAPABLE) { ++ pcd->a_alt_hnp_support = 1; ++ dwc_otg_pcd_update_otg(pcd, 0); ++ } else { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ } ++ break; ++ } ++ do_setup_in_status_phase(pcd); ++ break; ++ ++ case UT_INTERFACE: ++ do_gadget_setup(pcd, &ctrl); ++ break; ++ ++ case UT_ENDPOINT: ++ if (UGETW(ctrl.wValue) == UF_ENDPOINT_HALT) { ++ ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex)); ++ if (ep == 0) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ep->stopped = 1; ++ dwc_otg_ep_set_stall(core_if, &ep->dwc_ep); ++ } ++ do_setup_in_status_phase(pcd); ++ break; ++ } ++} ++ ++/** ++ * This function process the CLEAR_FEATURE Setup Commands. ++ */ ++static inline void do_clear_feature(dwc_otg_pcd_t * pcd) ++{ ++ usb_device_request_t ctrl = pcd->setup_pkt->req; ++ dwc_otg_pcd_ep_t *ep = 0; ++ ++ DWC_DEBUGPL(DBG_PCD, ++ "CLEAR_FEATURE:%02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bmRequestType, ctrl.bRequest, ++ UGETW(ctrl.wValue), UGETW(ctrl.wIndex), ++ UGETW(ctrl.wLength)); ++ ++ switch (UT_GET_RECIPIENT(ctrl.bmRequestType)) { ++ case UT_DEVICE: ++ switch (UGETW(ctrl.wValue)) { ++ case UF_DEVICE_REMOTE_WAKEUP: ++ pcd->remote_wakeup_enable = 0; ++ break; ++ ++ case UF_TEST_MODE: ++ /** @todo Add CLEAR_FEATURE for TEST modes. */ ++ break; ++ } ++ do_setup_in_status_phase(pcd); ++ break; ++ ++ case UT_ENDPOINT: ++ ep = get_ep_by_addr(pcd, UGETW(ctrl.wIndex)); ++ if (ep == 0) { ++ ep0_do_stall(pcd, -DWC_E_NOT_SUPPORTED); ++ return; ++ } ++ ++ pcd_clear_halt(pcd, ep); ++ ++ break; ++ } ++} ++ ++/** ++ * This function process the SET_ADDRESS Setup Commands. ++ */ ++static inline void do_set_address(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if; ++ usb_device_request_t ctrl = pcd->setup_pkt->req; ++ ++ if (ctrl.bmRequestType == UT_DEVICE) { ++ dcfg_data_t dcfg = {.d32 = 0 }; ++ ++#ifdef DEBUG_EP0 ++// DWC_DEBUGPL(DBG_PCDV, "SET_ADDRESS:%d\n", ctrl.wValue); ++#endif ++ dcfg.b.devaddr = UGETW(ctrl.wValue); ++ dwc_modify_reg32(&dev_if->dev_global_regs->dcfg, 0, dcfg.d32); ++ do_setup_in_status_phase(pcd); ++ } ++} ++ ++/** ++ * This function processes SETUP commands. In Linux, the USB Command ++ * processing is done in two places - the first being the PCD and the ++ * second in the Gadget Driver (for example, the File-Backed Storage ++ * Gadget Driver). ++ * ++ * <table> ++ * <tr><td>Command </td><td>Driver </td><td>Description</td></tr> ++ * ++ * <tr><td>GET_STATUS </td><td>PCD </td><td>Command is processed as ++ * defined in chapter 9 of the USB 2.0 Specification chapter 9 ++ * </td></tr> ++ * ++ * <tr><td>CLEAR_FEATURE </td><td>PCD </td><td>The Device and Endpoint ++ * requests are the ENDPOINT_HALT feature is procesed, all others the ++ * interface requests are ignored.</td></tr> ++ * ++ * <tr><td>SET_FEATURE </td><td>PCD </td><td>The Device and Endpoint ++ * requests are processed by the PCD. Interface requests are passed ++ * to the Gadget Driver.</td></tr> ++ * ++ * <tr><td>SET_ADDRESS </td><td>PCD </td><td>Program the DCFG reg, ++ * with device address received </td></tr> ++ * ++ * <tr><td>GET_DESCRIPTOR </td><td>Gadget Driver </td><td>Return the ++ * requested descriptor</td></tr> ++ * ++ * <tr><td>SET_DESCRIPTOR </td><td>Gadget Driver </td><td>Optional - ++ * not implemented by any of the existing Gadget Drivers.</td></tr> ++ * ++ * <tr><td>SET_CONFIGURATION </td><td>Gadget Driver </td><td>Disable ++ * all EPs and enable EPs for new configuration.</td></tr> ++ * ++ * <tr><td>GET_CONFIGURATION </td><td>Gadget Driver </td><td>Return ++ * the current configuration</td></tr> ++ * ++ * <tr><td>SET_INTERFACE </td><td>Gadget Driver </td><td>Disable all ++ * EPs and enable EPs for new configuration.</td></tr> ++ * ++ * <tr><td>GET_INTERFACE </td><td>Gadget Driver </td><td>Return the ++ * current interface.</td></tr> ++ * ++ * <tr><td>SYNC_FRAME </td><td>PCD </td><td>Display debug ++ * message.</td></tr> ++ * </table> ++ * ++ * When the SETUP Phase Done interrupt occurs, the PCD SETUP commands are ++ * processed by pcd_setup. Calling the Function Driver's setup function from ++ * pcd_setup processes the gadget SETUP commands. ++ */ ++static inline void pcd_setup(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ usb_device_request_t ctrl = pcd->setup_pkt->req; ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ ++ deptsiz0_data_t doeptsize0 = {.d32 = 0 }; ++ ++#ifdef DWC_UTE_CFI ++ int retval = 0; ++ struct cfi_usb_ctrlrequest cfi_req; ++#endif ++ ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "SETUP %02x.%02x v%04x i%04x l%04x\n", ++ ctrl.bmRequestType, ctrl.bRequest, ++ UGETW(ctrl.wValue), UGETW(ctrl.wIndex), ++ UGETW(ctrl.wLength)); ++#endif ++ ++ doeptsize0.d32 = dwc_read_reg32(&dev_if->out_ep_regs[0]->doeptsiz); ++ ++ /** @todo handle > 1 setup packet , assert error for now */ ++ ++ if (core_if->dma_enable && core_if->dma_desc_enable == 0 ++ && (doeptsize0.b.supcnt < 2)) { ++ DWC_ERROR ++ ("\n\n----------- CANNOT handle > 1 setup packet in DMA mode\n\n"); ++ } ++ ++ /* Clean up the request queue */ ++ dwc_otg_request_nuke(ep0); ++ ep0->stopped = 0; ++ ++ if (ctrl.bmRequestType & UE_DIR_IN) { ++ ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_DATA_PHASE; ++ } else { ++ ep0->dwc_ep.is_in = 0; ++ pcd->ep0state = EP0_OUT_DATA_PHASE; ++ } ++ ++ if (UGETW(ctrl.wLength) == 0) { ++ ep0->dwc_ep.is_in = 1; ++ pcd->ep0state = EP0_IN_STATUS_PHASE; ++ } ++ ++ if (UT_GET_TYPE(ctrl.bmRequestType) != UT_STANDARD) { ++ ++#ifdef DWC_UTE_CFI ++ DWC_MEMCPY(&cfi_req, &ctrl, sizeof(usb_device_request_t)); ++ ++ //printk(KERN_ALERT "CFI: req_type=0x%02x; req=0x%02x\n", ctrl.bRequestType, ctrl.bRequest); ++ if (UT_GET_TYPE(cfi_req.bRequestType) == UT_VENDOR) { ++ if (cfi_req.bRequest > 0xB0 && cfi_req.bRequest < 0xBF) { ++ retval = cfi_setup(pcd, &cfi_req); ++ if (retval < 0) { ++ ep0_do_stall(pcd, retval); ++ pcd->ep0_pending = 0; ++ return; ++ } ++ ++ /* if need gadget setup then call it and check the retval */ ++ if (pcd->cfi->need_gadget_att) { ++ retval = ++ cfi_gadget_setup(pcd, ++ &pcd->cfi-> ++ ctrl_req); ++ if (retval < 0) { ++ pcd->ep0_pending = 0; ++ return; ++ } ++ } ++ ++ if (pcd->cfi->need_status_in_complete) { ++ do_setup_in_status_phase(pcd); ++ } ++ return; ++ } ++ } ++#endif ++ ++ /* handle non-standard (class/vendor) requests in the gadget driver */ ++ do_gadget_setup(pcd, &ctrl); ++ return; ++ } ++ ++ /** @todo NGS: Handle bad setup packet? */ ++ ++/////////////////////////////////////////// ++//// --- Standard Request handling --- //// ++ ++ switch (ctrl.bRequest) { ++ case UR_GET_STATUS: ++ do_get_status(pcd); ++ break; ++ ++ case UR_CLEAR_FEATURE: ++ do_clear_feature(pcd); ++ break; ++ ++ case UR_SET_FEATURE: ++ do_set_feature(pcd); ++ break; ++ ++ case UR_SET_ADDRESS: ++ do_set_address(pcd); ++ break; ++ ++ case UR_SET_INTERFACE: ++ case UR_SET_CONFIG: ++// _pcd->request_config = 1; /* Configuration changed */ ++ do_gadget_setup(pcd, &ctrl); ++ break; ++ ++ case UR_SYNCH_FRAME: ++ do_gadget_setup(pcd, &ctrl); ++ break; ++ ++ default: ++ /* Call the Gadget Driver's setup functions */ ++ do_gadget_setup(pcd, &ctrl); ++ break; ++ } ++} ++ ++/** ++ * This function completes the ep0 control transfer. ++ */ ++static int32_t ep0_complete_request(dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dwc_otg_dev_in_ep_regs_t *in_ep_regs = ++ dev_if->in_ep_regs[ep->dwc_ep.num]; ++#ifdef DEBUG_EP0 ++ dwc_otg_dev_out_ep_regs_t *out_ep_regs = ++ dev_if->out_ep_regs[ep->dwc_ep.num]; ++#endif ++ deptsiz0_data_t deptsiz; ++ dev_dma_desc_sts_t desc_sts; ++ dwc_otg_pcd_request_t *req; ++ int is_last = 0; ++ dwc_otg_pcd_t *pcd = ep->pcd; ++ ++#ifdef DWC_UTE_CFI ++ struct cfi_usb_ctrlrequest *ctrlreq; ++ int retval = -DWC_E_NOT_SUPPORTED; ++#endif ++ ++ if (pcd->ep0_pending && DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ if (ep->dwc_ep.is_in) { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "Do setup OUT status phase\n"); ++#endif ++ do_setup_out_status_phase(pcd); ++ } else { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "Do setup IN status phase\n"); ++#endif ++ ++#ifdef DWC_UTE_CFI ++ ctrlreq = &pcd->cfi->ctrl_req; ++ ++ if (UT_GET_TYPE(ctrlreq->bRequestType) == UT_VENDOR) { ++ if (ctrlreq->bRequest > 0xB0 ++ && ctrlreq->bRequest < 0xBF) { ++ ++ /* Return if the PCD failed to handle the request */ ++ if ((retval = ++ pcd->cfi->ops. ++ ctrl_write_complete(pcd->cfi, ++ pcd)) < 0) { ++ CFI_INFO ++ ("ERROR setting a new value in the PCD(%d)\n", ++ retval); ++ ep0_do_stall(pcd, retval); ++ pcd->ep0_pending = 0; ++ return 0; ++ } ++ ++ /* If the gadget needs to be notified on the request */ ++ if (pcd->cfi->need_gadget_att == 1) { ++ //retval = do_gadget_setup(pcd, &pcd->cfi->ctrl_req); ++ retval = ++ cfi_gadget_setup(pcd, ++ &pcd->cfi-> ++ ctrl_req); ++ ++ /* Return from the function if the gadget failed to process ++ * the request properly - this should never happen !!! ++ */ ++ if (retval < 0) { ++ CFI_INFO ++ ("ERROR setting a new value in the gadget(%d)\n", ++ retval); ++ pcd->ep0_pending = 0; ++ return 0; ++ } ++ } ++ ++ CFI_INFO("%s: RETVAL=%d\n", __func__, ++ retval); ++ /* If we hit here then the PCD and the gadget has properly ++ * handled the request - so send the ZLP IN to the host. ++ */ ++ /* @todo: MAS - decide whether we need to start the setup ++ * stage based on the need_setup value of the cfi object ++ */ ++ do_setup_in_status_phase(pcd); ++ pcd->ep0_pending = 0; ++ return 1; ++ } ++ } ++#endif ++ ++ do_setup_in_status_phase(pcd); ++ } ++ pcd->ep0_pending = 0; ++ return 1; ++ } ++ ++ if (DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ return 0; ++ } ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ ++ if (pcd->ep0state == EP0_OUT_STATUS_PHASE ++ || pcd->ep0state == EP0_IN_STATUS_PHASE) { ++ is_last = 1; ++ } else if (ep->dwc_ep.is_in) { ++ deptsiz.d32 = dwc_read_reg32(&in_ep_regs->dieptsiz); ++ if (core_if->dma_desc_enable != 0) ++ desc_sts = dev_if->in_desc_addr->status; ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "%d len=%d xfersize=%d pktcnt=%d\n", ++ ep->dwc_ep.num, ep->dwc_ep.xfer_len, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++#endif ++ ++ if (((core_if->dma_desc_enable == 0) ++ && (deptsiz.b.xfersize == 0)) ++ || ((core_if->dma_desc_enable != 0) ++ && (desc_sts.b.bytes == 0))) { ++ req->actual = ep->dwc_ep.xfer_count; ++ /* Is a Zero Len Packet needed? */ ++ if (req->sent_zlp) { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "Setup Rx ZLP\n"); ++#endif ++ req->sent_zlp = 0; ++ } ++ do_setup_out_status_phase(pcd); ++ } ++ } else { ++ /* ep0-OUT */ ++#ifdef DEBUG_EP0 ++ deptsiz.d32 = dwc_read_reg32(&out_ep_regs->doeptsiz); ++ DWC_DEBUGPL(DBG_PCDV, "%d len=%d xsize=%d pktcnt=%d\n", ++ ep->dwc_ep.num, ep->dwc_ep.xfer_len, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++#endif ++ req->actual = ep->dwc_ep.xfer_count; ++ ++ /* Is a Zero Len Packet needed? */ ++ if (req->sent_zlp) { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "Setup Tx ZLP\n"); ++#endif ++ req->sent_zlp = 0; ++ } ++ if (core_if->dma_desc_enable == 0) ++ do_setup_in_status_phase(pcd); ++ } ++ ++ /* Complete the request */ ++ if (is_last) { ++ dwc_otg_request_done(ep, req, 0); ++ ep->dwc_ep.start_xfer_buff = 0; ++ ep->dwc_ep.xfer_buff = 0; ++ ep->dwc_ep.xfer_len = 0; ++ return 1; ++ } ++ return 0; ++} ++ ++#ifdef DWC_UTE_CFI ++/** ++ * This function calculates traverses all the CFI DMA descriptors and ++ * and accumulates the bytes that are left to be transfered. ++ * ++ * @return The total bytes left to transfered, or a negative value as failure ++ */ ++static inline int cfi_calc_desc_residue(dwc_otg_pcd_ep_t * ep) ++{ ++ int32_t ret = 0; ++ int i; ++ struct dwc_otg_dma_desc *ddesc = NULL; ++ struct cfi_ep *cfiep; ++ ++ /* See if the pcd_ep has its respective cfi_ep mapped */ ++ cfiep = get_cfi_ep_by_pcd_ep(ep->pcd->cfi, ep); ++ if (!cfiep) { ++ CFI_INFO("%s: Failed to find ep\n", __func__); ++ return -1; ++ } ++ ++ ddesc = ep->dwc_ep.descs; ++ ++ for (i = 0; (i < cfiep->desc_count) && (i < MAX_DMA_DESCS_PER_EP); i++) { ++ ++#if defined(PRINT_CFI_DMA_DESCS) ++ print_desc(ddesc, ep->ep.name, i); ++#endif ++ ret += ddesc->status.b.bytes; ++ ddesc++; ++ } ++ ++ if (ret) ++ CFI_INFO("!!!!!!!!!! WARNING (%s) - residue=%d\n", __func__, ++ ret); ++ ++ return ret; ++} ++#endif ++ ++/** ++ * This function completes the request for the EP. If there are ++ * additional requests for the EP in the queue they will be started. ++ */ ++static void complete_ep(dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ dwc_otg_dev_in_ep_regs_t *in_ep_regs = ++ dev_if->in_ep_regs[ep->dwc_ep.num]; ++ deptsiz_data_t deptsiz; ++ dev_dma_desc_sts_t desc_sts; ++ dwc_otg_pcd_request_t *req = 0; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ uint32_t byte_count = 0; ++ int is_last = 0; ++ int i; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s() %d-%s\n", __func__, ep->dwc_ep.num, ++ (ep->dwc_ep.is_in ? "IN" : "OUT")); ++ ++ /* Get any pending requests */ ++ if (!DWC_CIRCLEQ_EMPTY(&ep->queue)) { ++ req = DWC_CIRCLEQ_FIRST(&ep->queue); ++ if (!req) { ++ DWC_PRINTF("complete_ep 0x%p, req = NULL!\n", ep); ++ return; ++ } ++ } else { ++ DWC_PRINTF("complete_ep 0x%p, ep->queue empty!\n", ep); ++ return; ++ } ++ ++ DWC_DEBUGPL(DBG_PCD, "Requests %d\n", ep->pcd->request_pending); ++ ++ if (ep->dwc_ep.is_in) { ++ deptsiz.d32 = dwc_read_reg32(&in_ep_regs->dieptsiz); ++ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable == 0) { ++ if (deptsiz.b.xfersize == 0 ++ && deptsiz.b.pktcnt == 0) { ++ byte_count = ++ ep->dwc_ep.xfer_len - ++ ep->dwc_ep.xfer_count; ++ ++ ep->dwc_ep.xfer_buff += byte_count; ++ ep->dwc_ep.dma_addr += byte_count; ++ ep->dwc_ep.xfer_count += byte_count; ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "%d-%s len=%d xfersize=%d pktcnt=%d\n", ++ ep->dwc_ep.num, ++ (ep->dwc_ep. ++ is_in ? "IN" : "OUT"), ++ ep->dwc_ep.xfer_len, ++ deptsiz.b.xfersize, ++ deptsiz.b.pktcnt); ++ ++ if (ep->dwc_ep.xfer_len < ++ ep->dwc_ep.total_len) { ++ dwc_otg_ep_start_transfer ++ (core_if, &ep->dwc_ep); ++ } else if (ep->dwc_ep.sent_zlp) { ++ /* ++ * This fragment of code should initiate 0 ++ * length trasfer in case if it is queued ++ * a trasfer with size divisible to EPs max ++ * packet size and with usb_request zero field ++ * is set, which means that after data is transfered, ++ * it is also should be transfered ++ * a 0 length packet at the end. For Slave and ++ * Buffer DMA modes in this case SW has ++ * to initiate 2 transfers one with transfer size, ++ * and the second with 0 size. For Desriptor ++ * DMA mode SW is able to initiate a transfer, ++ * which will handle all the packets including ++ * the last 0 legth. ++ */ ++ ep->dwc_ep.sent_zlp = 0; ++ dwc_otg_ep_start_zl_transfer ++ (core_if, &ep->dwc_ep); ++ } else { ++ is_last = 1; ++ } ++ } else { ++ DWC_WARN ++ ("Incomplete transfer (%d - %s [siz=%d pkt=%d])\n", ++ ep->dwc_ep.num, ++ (ep->dwc_ep.is_in ? "IN" : "OUT"), ++ deptsiz.b.xfersize, ++ deptsiz.b.pktcnt); ++ } ++ } else { ++ dma_desc = ep->dwc_ep.desc_addr; ++ byte_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ++#ifdef DWC_UTE_CFI ++ CFI_INFO("%s: BUFFER_MODE=%d\n", __func__, ++ ep->dwc_ep.buff_mode); ++ if (ep->dwc_ep.buff_mode != BM_STANDARD) { ++ int residue; ++ ++ residue = cfi_calc_desc_residue(ep); ++ if (residue < 0) ++ return; ++ ++ byte_count = residue; ++ } else { ++#endif ++ for (i = 0; i < ep->dwc_ep.desc_cnt; ++ ++i) { ++ desc_sts = dma_desc->status; ++ byte_count += desc_sts.b.bytes; ++ dma_desc++; ++ } ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ if (byte_count == 0) { ++ ep->dwc_ep.xfer_count = ++ ep->dwc_ep.total_len; ++ is_last = 1; ++ } else { ++ DWC_WARN("Incomplete transfer\n"); ++ } ++ } ++ } else { ++ if (deptsiz.b.xfersize == 0 && deptsiz.b.pktcnt == 0) { ++ DWC_DEBUGPL(DBG_PCDV, ++ "%d-%s len=%d xfersize=%d pktcnt=%d\n", ++ ep->dwc_ep.num, ++ ep->dwc_ep.is_in ? "IN" : "OUT", ++ ep->dwc_ep.xfer_len, ++ deptsiz.b.xfersize, ++ deptsiz.b.pktcnt); ++ ++ /* Check if the whole transfer was completed, ++ * if no, setup transfer for next portion of data ++ */ ++ if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) { ++ dwc_otg_ep_start_transfer(core_if, ++ &ep->dwc_ep); ++ } else if (ep->dwc_ep.sent_zlp) { ++ /* ++ * This fragment of code should initiate 0 ++ * length trasfer in case if it is queued ++ * a trasfer with size divisible to EPs max ++ * packet size and with usb_request zero field ++ * is set, which means that after data is transfered, ++ * it is also should be transfered ++ * a 0 length packet at the end. For Slave and ++ * Buffer DMA modes in this case SW has ++ * to initiate 2 transfers one with transfer size, ++ * and the second with 0 size. For Desriptor ++ * DMA mode SW is able to initiate a transfer, ++ * which will handle all the packets including ++ * the last 0 legth. ++ */ ++ ep->dwc_ep.sent_zlp = 0; ++ dwc_otg_ep_start_zl_transfer(core_if, ++ &ep-> ++ dwc_ep); ++ } else { ++ is_last = 1; ++ } ++ } else { ++ DWC_WARN ++ ("Incomplete transfer (%d-%s [siz=%d pkt=%d])\n", ++ ep->dwc_ep.num, ++ (ep->dwc_ep.is_in ? "IN" : "OUT"), ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ } ++ } ++ } else { ++ dwc_otg_dev_out_ep_regs_t *out_ep_regs = ++ dev_if->out_ep_regs[ep->dwc_ep.num]; ++ desc_sts.d32 = 0; ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable) { ++ dma_desc = ep->dwc_ep.desc_addr; ++ byte_count = 0; ++ ep->dwc_ep.sent_zlp = 0; ++ ++#ifdef DWC_UTE_CFI ++ CFI_INFO("%s: BUFFER_MODE=%d\n", __func__, ++ ep->dwc_ep.buff_mode); ++ if (ep->dwc_ep.buff_mode != BM_STANDARD) { ++ int residue; ++ residue = cfi_calc_desc_residue(ep); ++ if (residue < 0) ++ return; ++ byte_count = residue; ++ } else { ++#endif ++ ++ for (i = 0; i < ep->dwc_ep.desc_cnt; ++ ++i) { ++ desc_sts = dma_desc->status; ++ byte_count += desc_sts.b.bytes; ++ dma_desc++; ++ } ++ ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ ep->dwc_ep.xfer_count = ep->dwc_ep.total_len ++ - byte_count + ++ ((4 - (ep->dwc_ep.total_len & 0x3)) & 0x3); ++ is_last = 1; ++ } else { ++ deptsiz.d32 = 0; ++ deptsiz.d32 = ++ dwc_read_reg32(&out_ep_regs->doeptsiz); ++ ++ byte_count = (ep->dwc_ep.xfer_len - ++ ep->dwc_ep.xfer_count - ++ deptsiz.b.xfersize); ++ ep->dwc_ep.xfer_buff += byte_count; ++ ep->dwc_ep.dma_addr += byte_count; ++ ep->dwc_ep.xfer_count += byte_count; ++ ++ /* Check if the whole transfer was completed, ++ * if no, setup transfer for next portion of data ++ */ ++ if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) { ++ dwc_otg_ep_start_transfer(core_if, ++ &ep->dwc_ep); ++ } else if (ep->dwc_ep.sent_zlp) { ++ /* ++ * This fragment of code should initiate 0 ++ * length trasfer in case if it is queued ++ * a trasfer with size divisible to EPs max ++ * packet size and with usb_request zero field ++ * is set, which means that after data is transfered, ++ * it is also should be transfered ++ * a 0 length packet at the end. For Slave and ++ * Buffer DMA modes in this case SW has ++ * to initiate 2 transfers one with transfer size, ++ * and the second with 0 size. For Desriptor ++ * DMA mode SW is able to initiate a transfer, ++ * which will handle all the packets including ++ * the last 0 legth. ++ */ ++ ep->dwc_ep.sent_zlp = 0; ++ dwc_otg_ep_start_zl_transfer(core_if, ++ &ep-> ++ dwc_ep); ++ } else { ++ is_last = 1; ++ } ++ } ++ } else { ++ /* Check if the whole transfer was completed, ++ * if no, setup transfer for next portion of data ++ */ ++ if (ep->dwc_ep.xfer_len < ep->dwc_ep.total_len) { ++ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep); ++ } else if (ep->dwc_ep.sent_zlp) { ++ /* ++ * This fragment of code should initiate 0 ++ * length trasfer in case if it is queued ++ * a trasfer with size divisible to EPs max ++ * packet size and with usb_request zero field ++ * is set, which means that after data is transfered, ++ * it is also should be transfered ++ * a 0 length packet at the end. For Slave and ++ * Buffer DMA modes in this case SW has ++ * to initiate 2 transfers one with transfer size, ++ * and the second with 0 size. For Desriptor ++ * DMA mode SW is able to initiate a transfer, ++ * which will handle all the packets including ++ * the last 0 legth. ++ */ ++ ep->dwc_ep.sent_zlp = 0; ++ dwc_otg_ep_start_zl_transfer(core_if, ++ &ep->dwc_ep); ++ } else { ++ is_last = 1; ++ } ++ } ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "addr %p, %d-%s len=%d cnt=%d xsize=%d pktcnt=%d\n", ++ &out_ep_regs->doeptsiz, ep->dwc_ep.num, ++ ep->dwc_ep.is_in ? "IN" : "OUT", ++ ep->dwc_ep.xfer_len, ep->dwc_ep.xfer_count, ++ deptsiz.b.xfersize, deptsiz.b.pktcnt); ++ } ++ ++ /* Complete the request */ ++ if (is_last) { ++#ifdef DWC_UTE_CFI ++ if (ep->dwc_ep.buff_mode != BM_STANDARD) { ++ req->actual = ep->dwc_ep.cfi_req_len - byte_count; ++ } else { ++#endif ++ req->actual = ep->dwc_ep.xfer_count; ++#ifdef DWC_UTE_CFI ++ } ++#endif ++ ++ dwc_otg_request_done(ep, req, 0); ++ ++ ep->dwc_ep.start_xfer_buff = 0; ++ ep->dwc_ep.xfer_buff = 0; ++ ep->dwc_ep.xfer_len = 0; ++ ++ /* If there is a request in the queue start it. */ ++ start_next_request(ep); ++ } ++} ++ ++#ifdef DWC_EN_ISOC ++ ++/** ++ * This function BNA interrupt for Isochronous EPs ++ * ++ */ ++static void dwc_otg_pcd_handle_iso_bna(dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_ep_t *dwc_ep = &ep->dwc_ep; ++ volatile uint32_t *addr; ++ depctl_data_t depctl = {.d32 = 0 }; ++ dwc_otg_pcd_t *pcd = ep->pcd; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ int i; ++ ++ dma_desc = ++ dwc_ep->iso_desc_addr + dwc_ep->desc_cnt * (dwc_ep->proc_buf_num); ++ ++ if (dwc_ep->is_in) { ++ dev_dma_desc_sts_t sts = {.d32 = 0 }; ++ for (i = 0; i < dwc_ep->desc_cnt; ++i, ++dma_desc) { ++ sts.d32 = dma_desc->status.d32; ++ sts.b_iso_in.bs = BS_HOST_READY; ++ dma_desc->status.d32 = sts.d32; ++ } ++ } else { ++ dev_dma_desc_sts_t sts = {.d32 = 0 }; ++ for (i = 0; i < dwc_ep->desc_cnt; ++i, ++dma_desc) { ++ sts.d32 = dma_desc->status.d32; ++ sts.b_iso_out.bs = BS_HOST_READY; ++ dma_desc->status.d32 = sts.d32; ++ } ++ } ++ ++ if (dwc_ep->is_in == 0) { ++ addr = ++ &GET_CORE_IF(pcd)->dev_if->out_ep_regs[dwc_ep->num]-> ++ doepctl; ++ } else { ++ addr = ++ &GET_CORE_IF(pcd)->dev_if->in_ep_regs[dwc_ep->num]->diepctl; ++ } ++ depctl.b.epena = 1; ++ dwc_modify_reg32(addr, depctl.d32, depctl.d32); ++} ++ ++/** ++ * This function sets latest iso packet information(non-PTI mode) ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++void set_current_pkt_info(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ deptsiz_data_t deptsiz = {.d32 = 0 }; ++ dma_addr_t dma_addr; ++ uint32_t offset; ++ ++ if (ep->proc_buf_num) ++ dma_addr = ep->dma_addr1; ++ else ++ dma_addr = ep->dma_addr0; ++ ++ if (ep->is_in) { ++ deptsiz.d32 = ++ dwc_read_reg32(&core_if->dev_if->in_ep_regs[ep->num]-> ++ dieptsiz); ++ offset = ep->data_per_frame; ++ } else { ++ deptsiz.d32 = ++ dwc_read_reg32(&core_if->dev_if->out_ep_regs[ep->num]-> ++ doeptsiz); ++ offset = ++ ep->data_per_frame + ++ (0x4 & (0x4 - (ep->data_per_frame & 0x3))); ++ } ++ ++ if (!deptsiz.b.xfersize) { ++ ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame; ++ ep->pkt_info[ep->cur_pkt].offset = ++ ep->cur_pkt_dma_addr - dma_addr; ++ ep->pkt_info[ep->cur_pkt].status = 0; ++ } else { ++ ep->pkt_info[ep->cur_pkt].length = ep->data_per_frame; ++ ep->pkt_info[ep->cur_pkt].offset = ++ ep->cur_pkt_dma_addr - dma_addr; ++ ep->pkt_info[ep->cur_pkt].status = -DWC_E_NO_DATA; ++ } ++ ep->cur_pkt_addr += offset; ++ ep->cur_pkt_dma_addr += offset; ++ ep->cur_pkt++; ++} ++ ++/** ++ * This function sets latest iso packet information(DDMA mode) ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dwc_ep The EP to start the transfer on. ++ * ++ */ ++static void set_ddma_iso_pkts_info(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * dwc_ep) ++{ ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ dev_dma_desc_sts_t sts = {.d32 = 0 }; ++ iso_pkt_info_t *iso_packet; ++ uint32_t data_per_desc; ++ uint32_t offset; ++ int i, j; ++ ++ iso_packet = dwc_ep->pkt_info; ++ ++ /** Reinit closed DMA Descriptors*/ ++ /** ISO OUT EP */ ++ if (dwc_ep->is_in == 0) { ++ dma_desc = ++ dwc_ep->iso_desc_addr + ++ dwc_ep->desc_cnt * dwc_ep->proc_buf_num; ++ offset = 0; ++ ++ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; ++ i += dwc_ep->pkt_per_frm) { ++ for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep-> ++ data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - ++ data_per_desc % ++ 4) : 0; ++ ++ sts.d32 = dma_desc->status.d32; ++ ++ /* Write status in iso_packet_decsriptor */ ++ iso_packet->status = ++ sts.b_iso_out.rxsts + ++ (sts.b_iso_out.bs ^ BS_DMA_DONE); ++ if (iso_packet->status) { ++ iso_packet->status = -DWC_E_NO_DATA; ++ } ++ ++ /* Received data length */ ++ if (!sts.b_iso_out.rxbytes) { ++ iso_packet->length = ++ data_per_desc - ++ sts.b_iso_out.rxbytes; ++ } else { ++ iso_packet->length = ++ data_per_desc - ++ sts.b_iso_out.rxbytes + (4 - ++ dwc_ep-> ++ data_per_frame ++ % 4); ++ } ++ ++ iso_packet->offset = offset; ++ ++ offset += data_per_desc; ++ dma_desc++; ++ iso_packet++; ++ } ++ } ++ ++ for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; ++ ++ sts.d32 = dma_desc->status.d32; ++ ++ /* Write status in iso_packet_decsriptor */ ++ iso_packet->status = ++ sts.b_iso_out.rxsts + ++ (sts.b_iso_out.bs ^ BS_DMA_DONE); ++ if (iso_packet->status) { ++ iso_packet->status = -DWC_E_NO_DATA; ++ } ++ ++ /* Received data length */ ++ iso_packet->length = ++ dwc_ep->data_per_frame - sts.b_iso_out.rxbytes; ++ ++ iso_packet->offset = offset; ++ ++ offset += data_per_desc; ++ iso_packet++; ++ dma_desc++; ++ } ++ ++ sts.d32 = dma_desc->status.d32; ++ ++ /* Write status in iso_packet_decsriptor */ ++ iso_packet->status = ++ sts.b_iso_out.rxsts + (sts.b_iso_out.bs ^ BS_DMA_DONE); ++ if (iso_packet->status) { ++ iso_packet->status = -DWC_E_NO_DATA; ++ } ++ /* Received data length */ ++ if (!sts.b_iso_out.rxbytes) { ++ iso_packet->length = ++ dwc_ep->data_per_frame - sts.b_iso_out.rxbytes; ++ } else { ++ iso_packet->length = ++ dwc_ep->data_per_frame - sts.b_iso_out.rxbytes + ++ (4 - dwc_ep->data_per_frame % 4); ++ } ++ ++ iso_packet->offset = offset; ++ } else { ++/** ISO IN EP */ ++ ++ dma_desc = ++ dwc_ep->iso_desc_addr + ++ dwc_ep->desc_cnt * dwc_ep->proc_buf_num; ++ ++ for (i = 0; i < dwc_ep->desc_cnt - 1; i++) { ++ sts.d32 = dma_desc->status.d32; ++ ++ /* Write status in iso packet descriptor */ ++ iso_packet->status = ++ sts.b_iso_in.txsts + ++ (sts.b_iso_in.bs ^ BS_DMA_DONE); ++ if (iso_packet->status != 0) { ++ iso_packet->status = -DWC_E_NO_DATA; ++ ++ } ++ /* Bytes has been transfered */ ++ iso_packet->length = ++ dwc_ep->data_per_frame - sts.b_iso_in.txbytes; ++ ++ dma_desc++; ++ iso_packet++; ++ } ++ ++ sts.d32 = dma_desc->status.d32; ++ while (sts.b_iso_in.bs == BS_DMA_BUSY) { ++ sts.d32 = dma_desc->status.d32; ++ } ++ ++ /* Write status in iso packet descriptor ??? do be done with ERROR codes */ ++ iso_packet->status = ++ sts.b_iso_in.txsts + (sts.b_iso_in.bs ^ BS_DMA_DONE); ++ if (iso_packet->status != 0) { ++ iso_packet->status = -DWC_E_NO_DATA; ++ } ++ ++ /* Bytes has been transfered */ ++ iso_packet->length = ++ dwc_ep->data_per_frame - sts.b_iso_in.txbytes; ++ } ++} ++ ++/** ++ * This function reinitialize DMA Descriptors for Isochronous transfer ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dwc_ep The EP to start the transfer on. ++ * ++ */ ++static void reinit_ddma_iso_xfer(dwc_otg_core_if_t * core_if, dwc_ep_t * dwc_ep) ++{ ++ int i, j; ++ dwc_otg_dev_dma_desc_t *dma_desc; ++ dma_addr_t dma_ad; ++ volatile uint32_t *addr; ++ dev_dma_desc_sts_t sts = {.d32 = 0 }; ++ uint32_t data_per_desc; ++ ++ if (dwc_ep->is_in == 0) { ++ addr = &core_if->dev_if->out_ep_regs[dwc_ep->num]->doepctl; ++ } else { ++ addr = &core_if->dev_if->in_ep_regs[dwc_ep->num]->diepctl; ++ } ++ ++ if (dwc_ep->proc_buf_num == 0) { ++ /** Buffer 0 descriptors setup */ ++ dma_ad = dwc_ep->dma_addr0; ++ } else { ++ /** Buffer 1 descriptors setup */ ++ dma_ad = dwc_ep->dma_addr1; ++ } ++ ++ /** Reinit closed DMA Descriptors*/ ++ /** ISO OUT EP */ ++ if (dwc_ep->is_in == 0) { ++ dma_desc = ++ dwc_ep->iso_desc_addr + ++ dwc_ep->desc_cnt * dwc_ep->proc_buf_num; ++ ++ sts.b_iso_out.bs = BS_HOST_READY; ++ sts.b_iso_out.rxsts = 0; ++ sts.b_iso_out.l = 0; ++ sts.b_iso_out.sp = 0; ++ sts.b_iso_out.ioc = 0; ++ sts.b_iso_out.pid = 0; ++ sts.b_iso_out.framenum = 0; ++ ++ for (i = 0; i < dwc_ep->desc_cnt - dwc_ep->pkt_per_frm; ++ i += dwc_ep->pkt_per_frm) { ++ for (j = 0; j < dwc_ep->pkt_per_frm; ++j) { ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep-> ++ data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - ++ data_per_desc % ++ 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ dma_ad += data_per_desc; ++ dma_desc++; ++ } ++ } ++ ++ for (j = 0; j < dwc_ep->pkt_per_frm - 1; ++j) { ++ ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ dma_desc++; ++ dma_ad += data_per_desc; ++ } ++ ++ sts.b_iso_out.ioc = 1; ++ sts.b_iso_out.l = dwc_ep->proc_buf_num; ++ ++ data_per_desc = ++ ((j + 1) * dwc_ep->maxpacket > ++ dwc_ep->data_per_frame) ? dwc_ep->data_per_frame - ++ j * dwc_ep->maxpacket : dwc_ep->maxpacket; ++ data_per_desc += ++ (data_per_desc % 4) ? (4 - data_per_desc % 4) : 0; ++ sts.b_iso_out.rxbytes = data_per_desc; ++ ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ } else { ++/** ISO IN EP */ ++ ++ dma_desc = ++ dwc_ep->iso_desc_addr + ++ dwc_ep->desc_cnt * dwc_ep->proc_buf_num; ++ ++ sts.b_iso_in.bs = BS_HOST_READY; ++ sts.b_iso_in.txsts = 0; ++ sts.b_iso_in.sp = 0; ++ sts.b_iso_in.ioc = 0; ++ sts.b_iso_in.pid = dwc_ep->pkt_per_frm; ++ sts.b_iso_in.framenum = dwc_ep->next_frame; ++ sts.b_iso_in.txbytes = dwc_ep->data_per_frame; ++ sts.b_iso_in.l = 0; ++ ++ for (i = 0; i < dwc_ep->desc_cnt - 1; i++) { ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ sts.b_iso_in.framenum += dwc_ep->bInterval; ++ dma_ad += dwc_ep->data_per_frame; ++ dma_desc++; ++ } ++ ++ sts.b_iso_in.ioc = 1; ++ sts.b_iso_in.l = dwc_ep->proc_buf_num; ++ ++ dma_desc->buf = dma_ad; ++ dma_desc->status.d32 = sts.d32; ++ ++ dwc_ep->next_frame = ++ sts.b_iso_in.framenum + dwc_ep->bInterval * 1; ++ } ++ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1; ++} ++ ++/** ++ * This function is to handle Iso EP transfer complete interrupt ++ * in case Iso out packet was dropped ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param dwc_ep The EP for wihich transfer complete was asserted ++ * ++ */ ++static uint32_t handle_iso_out_pkt_dropped(dwc_otg_core_if_t * core_if, ++ dwc_ep_t * dwc_ep) ++{ ++ uint32_t dma_addr; ++ uint32_t drp_pkt; ++ uint32_t drp_pkt_cnt; ++ deptsiz_data_t deptsiz = {.d32 = 0 }; ++ depctl_data_t depctl = {.d32 = 0 }; ++ int i; ++ ++ deptsiz.d32 = ++ dwc_read_reg32(&core_if->dev_if->out_ep_regs[dwc_ep->num]-> ++ doeptsiz); ++ ++ drp_pkt = dwc_ep->pkt_cnt - deptsiz.b.pktcnt; ++ drp_pkt_cnt = dwc_ep->pkt_per_frm - (drp_pkt % dwc_ep->pkt_per_frm); ++ ++ /* Setting dropped packets status */ ++ for (i = 0; i < drp_pkt_cnt; ++i) { ++ dwc_ep->pkt_info[drp_pkt].status = -DWC_E_NO_DATA; ++ drp_pkt++; ++ deptsiz.b.pktcnt--; ++ } ++ ++ if (deptsiz.b.pktcnt > 0) { ++ deptsiz.b.xfersize = ++ dwc_ep->xfer_len - (dwc_ep->pkt_cnt - ++ deptsiz.b.pktcnt) * dwc_ep->maxpacket; ++ } else { ++ deptsiz.b.xfersize = 0; ++ deptsiz.b.pktcnt = 0; ++ } ++ ++ dwc_write_reg32(&core_if->dev_if->out_ep_regs[dwc_ep->num]->doeptsiz, ++ deptsiz.d32); ++ ++ if (deptsiz.b.pktcnt > 0) { ++ if (dwc_ep->proc_buf_num) { ++ dma_addr = ++ dwc_ep->dma_addr1 + dwc_ep->xfer_len - ++ deptsiz.b.xfersize; ++ } else { ++ dma_addr = ++ dwc_ep->dma_addr0 + dwc_ep->xfer_len - ++ deptsiz.b.xfersize;; ++ } ++ ++ dwc_write_reg32(&core_if->dev_if->out_ep_regs[dwc_ep->num]-> ++ doepdma, dma_addr); ++ ++ /** Re-enable endpoint, clear nak */ ++ depctl.d32 = 0; ++ depctl.b.epena = 1; ++ depctl.b.cnak = 1; ++ ++ dwc_modify_reg32(&core_if->dev_if->out_ep_regs[dwc_ep->num]-> ++ doepctl, depctl.d32, depctl.d32); ++ return 0; ++ } else { ++ return 1; ++ } ++} ++ ++/** ++ * This function sets iso packets information(PTI mode) ++ * ++ * @param core_if Programming view of DWC_otg controller. ++ * @param ep The EP to start the transfer on. ++ * ++ */ ++static uint32_t set_iso_pkts_info(dwc_otg_core_if_t * core_if, dwc_ep_t * ep) ++{ ++ int i, j; ++ dma_addr_t dma_ad; ++ iso_pkt_info_t *packet_info = ep->pkt_info; ++ uint32_t offset; ++ uint32_t frame_data; ++ deptsiz_data_t deptsiz; ++ ++ if (ep->proc_buf_num == 0) { ++ /** Buffer 0 descriptors setup */ ++ dma_ad = ep->dma_addr0; ++ } else { ++ /** Buffer 1 descriptors setup */ ++ dma_ad = ep->dma_addr1; ++ } ++ ++ if (ep->is_in) { ++ deptsiz.d32 = ++ dwc_read_reg32(&core_if->dev_if->in_ep_regs[ep->num]-> ++ dieptsiz); ++ } else { ++ deptsiz.d32 = ++ dwc_read_reg32(&core_if->dev_if->out_ep_regs[ep->num]-> ++ doeptsiz); ++ } ++ ++ if (!deptsiz.b.xfersize) { ++ offset = 0; ++ for (i = 0; i < ep->pkt_cnt; i += ep->pkt_per_frm) { ++ frame_data = ep->data_per_frame; ++ for (j = 0; j < ep->pkt_per_frm; ++j) { ++ ++ /* Packet status - is not set as initially ++ * it is set to 0 and if packet was sent ++ successfully, status field will remain 0*/ ++ ++ /* Bytes has been transfered */ ++ packet_info->length = ++ (ep->maxpacket < ++ frame_data) ? ep->maxpacket : frame_data; ++ ++ /* Received packet offset */ ++ packet_info->offset = offset; ++ offset += packet_info->length; ++ frame_data -= packet_info->length; ++ ++ packet_info++; ++ } ++ } ++ return 1; ++ } else { ++ /* This is a workaround for in case of Transfer Complete with ++ * PktDrpSts interrupts merging - in this case Transfer complete ++ * interrupt for Isoc Out Endpoint is asserted without PktDrpSts ++ * set and with DOEPTSIZ register non zero. Investigations showed, ++ * that this happens when Out packet is dropped, but because of ++ * interrupts merging during first interrupt handling PktDrpSts ++ * bit is cleared and for next merged interrupts it is not reset. ++ * In this case SW hadles the interrupt as if PktDrpSts bit is set. ++ */ ++ if (ep->is_in) { ++ return 1; ++ } else { ++ return handle_iso_out_pkt_dropped(core_if, ep); ++ } ++ } ++} ++ ++/** ++ * This function is to handle Iso EP transfer complete interrupt ++ * ++ * @param pcd The PCD ++ * @param ep The EP for which transfer complete was asserted ++ * ++ */ ++static void complete_iso_ep(dwc_otg_pcd_t * pcd, dwc_otg_pcd_ep_t * ep) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(ep->pcd); ++ dwc_ep_t *dwc_ep = &ep->dwc_ep; ++ uint8_t is_last = 0; ++ ++ if(ep->dwc_ep.next_frame == 0xffffffff) { ++ DWC_WARN("Next frame is not set!\n"); ++ return; ++ } ++ ++ if (core_if->dma_enable) { ++ if (core_if->dma_desc_enable) { ++ set_ddma_iso_pkts_info(core_if, dwc_ep); ++ reinit_ddma_iso_xfer(core_if, dwc_ep); ++ is_last = 1; ++ } else { ++ if (core_if->pti_enh_enable) { ++ if (set_iso_pkts_info(core_if, dwc_ep)) { ++ dwc_ep->proc_buf_num = ++ (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ dwc_otg_iso_ep_start_buf_transfer ++ (core_if, dwc_ep); ++ is_last = 1; ++ } ++ } else { ++ set_current_pkt_info(core_if, dwc_ep); ++ if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { ++ is_last = 1; ++ dwc_ep->cur_pkt = 0; ++ dwc_ep->proc_buf_num = ++ (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ if (dwc_ep->proc_buf_num) { ++ dwc_ep->cur_pkt_addr = ++ dwc_ep->xfer_buff1; ++ dwc_ep->cur_pkt_dma_addr = ++ dwc_ep->dma_addr1; ++ } else { ++ dwc_ep->cur_pkt_addr = ++ dwc_ep->xfer_buff0; ++ dwc_ep->cur_pkt_dma_addr = ++ dwc_ep->dma_addr0; ++ } ++ ++ } ++ dwc_otg_iso_ep_start_frm_transfer(core_if, ++ dwc_ep); ++ } ++ } ++ } else { ++ set_current_pkt_info(core_if, dwc_ep); ++ if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { ++ is_last = 1; ++ dwc_ep->cur_pkt = 0; ++ dwc_ep->proc_buf_num = (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ if (dwc_ep->proc_buf_num) { ++ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff1; ++ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr1; ++ } else { ++ dwc_ep->cur_pkt_addr = dwc_ep->xfer_buff0; ++ dwc_ep->cur_pkt_dma_addr = dwc_ep->dma_addr0; ++ } ++ ++ } ++ dwc_otg_iso_ep_start_frm_transfer(core_if, dwc_ep); ++ } ++ if (is_last) ++ dwc_otg_iso_buffer_done(pcd, ep, ep->iso_req_handle); ++} ++#endif /* DWC_EN_ISOC */ ++ ++/** ++ * This function handles EP0 Control transfers. ++ * ++ * The state of the control tranfers are tracked in ++ * <code>ep0state</code>. ++ */ ++static void handle_ep0(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_pcd_ep_t *ep0 = &pcd->ep0; ++ dev_dma_desc_sts_t desc_sts; ++ deptsiz0_data_t deptsiz; ++ uint32_t byte_count; ++ ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__); ++ print_ep0_state(pcd); ++#endif ++ ++// DWC_PRINTF("HANDLE EP0\n"); ++ ++ switch (pcd->ep0state) { ++ case EP0_DISCONNECT: ++ break; ++ ++ case EP0_IDLE: ++ pcd->request_config = 0; ++ ++ pcd_setup(pcd); ++ break; ++ ++ case EP0_IN_DATA_PHASE: ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "DATA_IN EP%d-%s: type=%d, mps=%d\n", ++ ep0->dwc_ep.num, (ep0->dwc_ep.is_in ? "IN" : "OUT"), ++ ep0->dwc_ep.type, ep0->dwc_ep.maxpacket); ++#endif ++ ++ if (core_if->dma_enable != 0) { ++ /* ++ * For EP0 we can only program 1 packet at a time so we ++ * need to do the make calculations after each complete. ++ * Call write_packet to make the calculations, as in ++ * slave mode, and use those values to determine if we ++ * can complete. ++ */ ++ if (core_if->dma_desc_enable == 0) { ++ deptsiz.d32 = ++ dwc_read_reg32(&core_if->dev_if-> ++ in_ep_regs[0]->dieptsiz); ++ byte_count = ++ ep0->dwc_ep.xfer_len - deptsiz.b.xfersize; ++ } else { ++ desc_sts = ++ core_if->dev_if->in_desc_addr->status; ++ byte_count = ++ ep0->dwc_ep.xfer_len - desc_sts.b.bytes; ++ } ++ ep0->dwc_ep.xfer_count += byte_count; ++ ep0->dwc_ep.xfer_buff += byte_count; ++ ep0->dwc_ep.dma_addr += byte_count; ++ } ++ if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) { ++ dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd), ++ &ep0->dwc_ep); ++ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n"); ++ } else if (ep0->dwc_ep.sent_zlp) { ++ dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd), ++ &ep0->dwc_ep); ++ ep0->dwc_ep.sent_zlp = 0; ++ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n"); ++ } else { ++ ep0_complete_request(ep0); ++ DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n"); ++ } ++ break; ++ case EP0_OUT_DATA_PHASE: ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "DATA_OUT EP%d-%s: type=%d, mps=%d\n", ++ ep0->dwc_ep.num, (ep0->dwc_ep.is_in ? "IN" : "OUT"), ++ ep0->dwc_ep.type, ep0->dwc_ep.maxpacket); ++#endif ++ if (core_if->dma_enable != 0) { ++ if (core_if->dma_desc_enable == 0) { ++ deptsiz.d32 = ++ dwc_read_reg32(&core_if->dev_if-> ++ out_ep_regs[0]->doeptsiz); ++ byte_count = ++ ep0->dwc_ep.maxpacket - deptsiz.b.xfersize; ++ } else { ++ desc_sts = ++ core_if->dev_if->out_desc_addr->status; ++ byte_count = ++ ep0->dwc_ep.maxpacket - desc_sts.b.bytes; ++ } ++ ep0->dwc_ep.xfer_count += byte_count; ++ ep0->dwc_ep.xfer_buff += byte_count; ++ ep0->dwc_ep.dma_addr += byte_count; ++ } ++ if (ep0->dwc_ep.xfer_count < ep0->dwc_ep.total_len) { ++ dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd), ++ &ep0->dwc_ep); ++ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n"); ++ } else if (ep0->dwc_ep.sent_zlp) { ++ dwc_otg_ep0_continue_transfer(GET_CORE_IF(pcd), ++ &ep0->dwc_ep); ++ ep0->dwc_ep.sent_zlp = 0; ++ DWC_DEBUGPL(DBG_PCD, "CONTINUE TRANSFER\n"); ++ } else { ++ ep0_complete_request(ep0); ++ DWC_DEBUGPL(DBG_PCD, "COMPLETE TRANSFER\n"); ++ } ++ break; ++ ++ case EP0_IN_STATUS_PHASE: ++ case EP0_OUT_STATUS_PHASE: ++ DWC_DEBUGPL(DBG_PCD, "CASE: EP0_STATUS\n"); ++ ep0_complete_request(ep0); ++ pcd->ep0state = EP0_IDLE; ++ ep0->stopped = 1; ++ ep0->dwc_ep.is_in = 0; /* OUT for next SETUP */ ++ ++ /* Prepare for more SETUP Packets */ ++ if (core_if->dma_enable) { ++ ep0_out_start(core_if, pcd); ++ } ++ break; ++ ++ case EP0_STALL: ++ DWC_ERROR("EP0 STALLed, should not get here pcd_setup()\n"); ++ break; ++ } ++#ifdef DEBUG_EP0 ++ print_ep0_state(pcd); ++#endif ++} ++ ++/** ++ * Restart transfer ++ */ ++static void restart_transfer(dwc_otg_pcd_t * pcd, const uint32_t epnum) ++{ ++ dwc_otg_core_if_t *core_if; ++ dwc_otg_dev_if_t *dev_if; ++ deptsiz_data_t dieptsiz = {.d32 = 0 }; ++ dwc_otg_pcd_ep_t *ep; ++ ++ ep = get_in_ep(pcd, epnum); ++ ++#ifdef DWC_EN_ISOC ++ if (ep->dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) { ++ return; ++ } ++#endif /* DWC_EN_ISOC */ ++ ++ core_if = GET_CORE_IF(pcd); ++ dev_if = core_if->dev_if; ++ ++ dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->dieptsiz); ++ ++ DWC_DEBUGPL(DBG_PCD, "xfer_buff=%p xfer_count=%0x xfer_len=%0x" ++ " stopped=%d\n", ep->dwc_ep.xfer_buff, ++ ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len, ep->stopped); ++ /* ++ * If xfersize is 0 and pktcnt in not 0, resend the last packet. ++ */ ++ if (dieptsiz.b.pktcnt && dieptsiz.b.xfersize == 0 && ++ ep->dwc_ep.start_xfer_buff != 0) { ++ if (ep->dwc_ep.total_len <= ep->dwc_ep.maxpacket) { ++ ep->dwc_ep.xfer_count = 0; ++ ep->dwc_ep.xfer_buff = ep->dwc_ep.start_xfer_buff; ++ ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count; ++ } else { ++ ep->dwc_ep.xfer_count -= ep->dwc_ep.maxpacket; ++ /* convert packet size to dwords. */ ++ ep->dwc_ep.xfer_buff -= ep->dwc_ep.maxpacket; ++ ep->dwc_ep.xfer_len = ep->dwc_ep.xfer_count; ++ } ++ ep->stopped = 0; ++ DWC_DEBUGPL(DBG_PCD, "xfer_buff=%p xfer_count=%0x " ++ "xfer_len=%0x stopped=%d\n", ++ ep->dwc_ep.xfer_buff, ++ ep->dwc_ep.xfer_count, ep->dwc_ep.xfer_len, ++ ep->stopped); ++ if (epnum == 0) { ++ dwc_otg_ep0_start_transfer(core_if, &ep->dwc_ep); ++ } else { ++ dwc_otg_ep_start_transfer(core_if, &ep->dwc_ep); ++ } ++ } ++} ++ ++/** ++ * handle the IN EP disable interrupt. ++ */ ++static inline void handle_in_ep_disable_intr(dwc_otg_pcd_t * pcd, ++ const uint32_t epnum) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ deptsiz_data_t dieptsiz = {.d32 = 0 }; ++ dctl_data_t dctl = {.d32 = 0 }; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ ++ ep = get_in_ep(pcd, epnum); ++ dwc_ep = &ep->dwc_ep; ++ ++ if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num); ++ return; ++ } ++ ++ DWC_DEBUGPL(DBG_PCD, "diepctl%d=%0x\n", epnum, ++ dwc_read_reg32(&dev_if->in_ep_regs[epnum]->diepctl)); ++ dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[epnum]->dieptsiz); ++ ++ DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n", ++ dieptsiz.b.pktcnt, dieptsiz.b.xfersize); ++ ++ if (ep->stopped) { ++ /* Flush the Tx FIFO */ ++ dwc_otg_flush_tx_fifo(core_if, dwc_ep->tx_fifo_num); ++ /* Clear the Global IN NP NAK */ ++ dctl.d32 = 0; ++ dctl.b.cgnpinnak = 1; ++ dwc_modify_reg32(&dev_if->dev_global_regs->dctl, dctl.d32, 0); ++ /* Restart the transaction */ ++ if (dieptsiz.b.pktcnt != 0 || dieptsiz.b.xfersize != 0) { ++ restart_transfer(pcd, epnum); ++ } ++ } else { ++ /* Restart the transaction */ ++ if (dieptsiz.b.pktcnt != 0 || dieptsiz.b.xfersize != 0) { ++ restart_transfer(pcd, epnum); ++ } ++ DWC_DEBUGPL(DBG_ANY, "STOPPED!!!\n"); ++ } ++} ++ ++/** ++ * Handler for the IN EP timeout handshake interrupt. ++ */ ++static inline void handle_in_ep_timeout_intr(dwc_otg_pcd_t * pcd, ++ const uint32_t epnum) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ ++#ifdef DEBUG ++ deptsiz_data_t dieptsiz = {.d32 = 0 }; ++ uint32_t num = 0; ++#endif ++ dctl_data_t dctl = {.d32 = 0 }; ++ dwc_otg_pcd_ep_t *ep; ++ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ ep = get_in_ep(pcd, epnum); ++ ++ /* Disable the NP Tx Fifo Empty Interrrupt */ ++ if (!core_if->dma_enable) { ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ } ++ /** @todo NGS Check EP type. ++ * Implement for Periodic EPs */ ++ /* ++ * Non-periodic EP ++ */ ++ /* Enable the Global IN NAK Effective Interrupt */ ++ intr_mask.b.ginnakeff = 1; ++ dwc_modify_reg32(&core_if->core_global_regs->gintmsk, 0, intr_mask.d32); ++ ++ /* Set Global IN NAK */ ++ dctl.b.sgnpinnak = 1; ++ dwc_modify_reg32(&dev_if->dev_global_regs->dctl, dctl.d32, dctl.d32); ++ ++ ep->stopped = 1; ++ ++#ifdef DEBUG ++ dieptsiz.d32 = dwc_read_reg32(&dev_if->in_ep_regs[num]->dieptsiz); ++ DWC_DEBUGPL(DBG_ANY, "pktcnt=%d size=%d\n", ++ dieptsiz.b.pktcnt, dieptsiz.b.xfersize); ++#endif ++ ++#ifdef DISABLE_PERIODIC_EP ++ /* ++ * Set the NAK bit for this EP to ++ * start the disable process. ++ */ ++ diepctl.d32 = 0; ++ diepctl.b.snak = 1; ++ dwc_modify_reg32(&dev_if->in_ep_regs[num]->diepctl, diepctl.d32, ++ diepctl.d32); ++ ep->disabling = 1; ++ ep->stopped = 1; ++#endif ++} ++ ++/** ++ * Handler for the IN EP NAK interrupt. ++ */ ++static inline int32_t handle_in_ep_nak_intr(dwc_otg_pcd_t * pcd, ++ const uint32_t epnum) ++{ ++ /** @todo implement ISR */ ++ dwc_otg_core_if_t *core_if; ++ diepmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "IN EP NAK"); ++ core_if = GET_CORE_IF(pcd); ++ intr_mask.b.nak = 1; ++ ++ if (core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs-> ++ diepeachintmsk[epnum], intr_mask.d32, 0); ++ } else { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->diepmsk, ++ intr_mask.d32, 0); ++ } ++ ++ return 1; ++} ++ ++/** ++ * Handler for the OUT EP Babble interrupt. ++ */ ++static inline int32_t handle_out_ep_babble_intr(dwc_otg_pcd_t * pcd, ++ const uint32_t epnum) ++{ ++ /** @todo implement ISR */ ++ dwc_otg_core_if_t *core_if; ++ doepmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", ++ "OUT EP Babble"); ++ core_if = GET_CORE_IF(pcd); ++ intr_mask.b.babble = 1; ++ ++ if (core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs-> ++ doepeachintmsk[epnum], intr_mask.d32, 0); ++ } else { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepmsk, ++ intr_mask.d32, 0); ++ } ++ ++ return 1; ++} ++ ++/** ++ * Handler for the OUT EP NAK interrupt. ++ */ ++static inline int32_t handle_out_ep_nak_intr(dwc_otg_pcd_t * pcd, ++ const uint32_t epnum) ++{ ++ /** @todo implement ISR */ ++ dwc_otg_core_if_t *core_if; ++ doepmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "OUT EP NAK"); ++ core_if = GET_CORE_IF(pcd); ++ intr_mask.b.nak = 1; ++ ++ if (core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs-> ++ doepeachintmsk[epnum], intr_mask.d32, 0); ++ } else { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepmsk, ++ intr_mask.d32, 0); ++ } ++ ++ return 1; ++} ++ ++/** ++ * Handler for the OUT EP NYET interrupt. ++ */ ++static inline int32_t handle_out_ep_nyet_intr(dwc_otg_pcd_t * pcd, ++ const uint32_t epnum) ++{ ++ /** @todo implement ISR */ ++ dwc_otg_core_if_t *core_if; ++ doepmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", "OUT EP NYET"); ++ core_if = GET_CORE_IF(pcd); ++ intr_mask.b.nyet = 1; ++ ++ if (core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs-> ++ doepeachintmsk[epnum], intr_mask.d32, 0); ++ } else { ++ dwc_modify_reg32(&core_if->dev_if->dev_global_regs->doepmsk, ++ intr_mask.d32, 0); ++ } ++ ++ return 1; ++} ++ ++/** ++ * This interrupt indicates that an IN EP has a pending Interrupt. ++ * The sequence for handling the IN EP interrupt is shown below: ++ * -# Read the Device All Endpoint Interrupt register ++ * -# Repeat the following for each IN EP interrupt bit set (from ++ * LSB to MSB). ++ * -# Read the Device Endpoint Interrupt (DIEPINTn) register ++ * -# If "Transfer Complete" call the request complete function ++ * -# If "Endpoint Disabled" complete the EP disable procedure. ++ * -# If "AHB Error Interrupt" log error ++ * -# If "Time-out Handshake" log error ++ * -# If "IN Token Received when TxFIFO Empty" write packet to Tx ++ * FIFO. ++ * -# If "IN Token EP Mismatch" (disable, this is handled by EP ++ * Mismatch Interrupt) ++ */ ++static int32_t dwc_otg_pcd_handle_in_ep_intr(dwc_otg_pcd_t * pcd) ++{ ++#define CLEAR_IN_EP_INTR(__core_if,__epnum,__intr) \ ++do { \ ++ diepint_data_t diepint = {.d32=0}; \ ++ diepint.b.__intr = 1; \ ++ dwc_write_reg32(&__core_if->dev_if->in_ep_regs[__epnum]->diepint, \ ++ diepint.d32); \ ++} while (0) ++ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ diepint_data_t diepint = {.d32 = 0 }; ++ dctl_data_t dctl = {.d32 = 0 }; ++ depctl_data_t depctl = {.d32 = 0 }; ++ uint32_t ep_intr; ++ uint32_t epnum = 0; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, pcd); ++ ++ /* Read in the device interrupt bits */ ++ ep_intr = dwc_otg_read_dev_all_in_ep_intr(core_if); ++ ++ /* Service the Device IN interrupts for each endpoint */ ++ while (ep_intr) { ++ if (ep_intr & 0x1) { ++ uint32_t empty_msk; ++ /* Get EP pointer */ ++ ep = get_in_ep(pcd, epnum); ++ dwc_ep = &ep->dwc_ep; ++ ++ depctl.d32 = ++ dwc_read_reg32(&dev_if->in_ep_regs[epnum]->diepctl); ++ empty_msk = ++ dwc_read_reg32(&dev_if->dev_global_regs-> ++ dtknqr4_fifoemptymsk); ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "IN EP INTERRUPT - %d\nepmty_msk - %8x diepctl - %8x\n", ++ epnum, empty_msk, depctl.d32); ++ ++ DWC_DEBUGPL(DBG_PCD, ++ "EP%d-%s: type=%d, mps=%d\n", ++ dwc_ep->num, (dwc_ep->is_in ? "IN" : "OUT"), ++ dwc_ep->type, dwc_ep->maxpacket); ++ ++ diepint.d32 = ++ dwc_otg_read_dev_in_ep_intr(core_if, dwc_ep); ++ ++ DWC_DEBUGPL(DBG_PCDV, ++ "EP %d Interrupt Register - 0x%x\n", epnum, ++ diepint.d32); ++ /* Transfer complete */ ++ if (diepint.b.xfercompl) { ++ /* Disable the NP Tx FIFO Empty ++ * Interrrupt */ ++ if (core_if->en_multiple_tx_fifo == 0) { ++ intr_mask.b.nptxfempty = 1; ++ dwc_modify_reg32(&core_if-> ++ core_global_regs-> ++ gintmsk, intr_mask.d32, ++ 0); ++ } else { ++ /* Disable the Tx FIFO Empty Interrupt for this EP */ ++ uint32_t fifoemptymsk = ++ 0x1 << dwc_ep->num; ++ dwc_modify_reg32(&core_if->dev_if-> ++ dev_global_regs-> ++ dtknqr4_fifoemptymsk, ++ fifoemptymsk, 0); ++ } ++ /* Clear the bit in DIEPINTn for this interrupt */ ++ CLEAR_IN_EP_INTR(core_if, epnum, xfercompl); ++ ++ /* Complete the transfer */ ++ if (epnum == 0) { ++ handle_ep0(pcd); ++ } ++#ifdef DWC_EN_ISOC ++ else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ if (!ep->stopped) ++ complete_iso_ep(pcd, ep); ++ } ++#endif /* DWC_EN_ISOC */ ++ else { ++ ++ complete_ep(ep); ++ } ++ } ++ /* Endpoint disable */ ++ if (diepint.b.epdisabled) { ++ DWC_DEBUGPL(DBG_ANY, "EP%d IN disabled\n", ++ epnum); ++ handle_in_ep_disable_intr(pcd, epnum); ++ ++ /* Clear the bit in DIEPINTn for this interrupt */ ++ CLEAR_IN_EP_INTR(core_if, epnum, epdisabled); ++ } ++ /* AHB Error */ ++ if (diepint.b.ahberr) { ++ DWC_DEBUGPL(DBG_ANY, "EP%d IN AHB Error\n", ++ epnum); ++ /* Clear the bit in DIEPINTn for this interrupt */ ++ CLEAR_IN_EP_INTR(core_if, epnum, ahberr); ++ } ++ /* TimeOUT Handshake (non-ISOC IN EPs) */ ++ if (diepint.b.timeout) { ++ DWC_DEBUGPL(DBG_ANY, "EP%d IN Time-out\n", ++ epnum); ++ handle_in_ep_timeout_intr(pcd, epnum); ++ ++ CLEAR_IN_EP_INTR(core_if, epnum, timeout); ++ } ++ /** IN Token received with TxF Empty */ ++ if (diepint.b.intktxfemp) { ++ DWC_DEBUGPL(DBG_ANY, ++ "EP%d IN TKN TxFifo Empty\n", ++ epnum); ++ if (!ep->stopped && epnum != 0) { ++ ++ diepmsk_data_t diepmsk = {.d32 = 0 }; ++ diepmsk.b.intktxfemp = 1; ++ ++ if (core_if->multiproc_int_enable) { ++ dwc_modify_reg32(&dev_if-> ++ dev_global_regs-> ++ diepeachintmsk ++ [epnum], ++ diepmsk.d32, ++ 0); ++ } else { ++ dwc_modify_reg32(&dev_if-> ++ dev_global_regs-> ++ diepmsk, ++ diepmsk.d32, ++ 0); ++ } ++ } else if (core_if->dma_desc_enable ++ && epnum == 0 ++ && pcd->ep0state == ++ EP0_OUT_STATUS_PHASE) { ++ // EP0 IN set STALL ++ depctl.d32 = ++ dwc_read_reg32(&dev_if-> ++ in_ep_regs[epnum]-> ++ diepctl); ++ ++ /* set the disable and stall bits */ ++ if (depctl.b.epena) { ++ depctl.b.epdis = 1; ++ } ++ depctl.b.stall = 1; ++ dwc_write_reg32(&dev_if-> ++ in_ep_regs[epnum]-> ++ diepctl, depctl.d32); ++ } ++ CLEAR_IN_EP_INTR(core_if, epnum, intktxfemp); ++ } ++ /** IN Token Received with EP mismatch */ ++ if (diepint.b.intknepmis) { ++ DWC_DEBUGPL(DBG_ANY, ++ "EP%d IN TKN EP Mismatch\n", epnum); ++ CLEAR_IN_EP_INTR(core_if, epnum, intknepmis); ++ } ++ /** IN Endpoint NAK Effective */ ++ if (diepint.b.inepnakeff) { ++ DWC_DEBUGPL(DBG_ANY, ++ "EP%d IN EP NAK Effective\n", ++ epnum); ++ /* Periodic EP */ ++ if (ep->disabling) { ++ depctl.d32 = 0; ++ depctl.b.snak = 1; ++ depctl.b.epdis = 1; ++ dwc_modify_reg32(&dev_if-> ++ in_ep_regs[epnum]-> ++ diepctl, depctl.d32, ++ depctl.d32); ++ } ++ CLEAR_IN_EP_INTR(core_if, epnum, inepnakeff); ++ ++ } ++ ++ /** IN EP Tx FIFO Empty Intr */ ++ if (diepint.b.emptyintr) { ++ DWC_DEBUGPL(DBG_ANY, ++ "EP%d Tx FIFO Empty Intr \n", ++ epnum); ++ write_empty_tx_fifo(pcd, epnum); ++ ++ CLEAR_IN_EP_INTR(core_if, epnum, emptyintr); ++ ++ } ++ ++ /** IN EP BNA Intr */ ++ if (diepint.b.bna) { ++ CLEAR_IN_EP_INTR(core_if, epnum, bna); ++ if (core_if->dma_desc_enable) { ++#ifdef DWC_EN_ISOC ++ if (dwc_ep->type == ++ DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * This checking is performed to prevent first "false" BNA ++ * handling occuring right after reconnect ++ */ ++ if (dwc_ep->next_frame != ++ 0xffffffff) ++ dwc_otg_pcd_handle_iso_bna ++ (ep); ++ } else ++#endif /* DWC_EN_ISOC */ ++ { ++ dctl.d32 = ++ dwc_read_reg32(&dev_if-> ++ dev_global_regs-> ++ dctl); ++ ++ /* If Global Continue on BNA is disabled - disable EP */ ++ if (!dctl.b.gcontbna) { ++ depctl.d32 = 0; ++ depctl.b.snak = 1; ++ depctl.b.epdis = 1; ++ dwc_modify_reg32 ++ (&dev_if-> ++ in_ep_regs[epnum]-> ++ diepctl, ++ depctl.d32, ++ depctl.d32); ++ } else { ++ start_next_request(ep); ++ } ++ } ++ } ++ } ++ /* NAK Interrutp */ ++ if (diepint.b.nak) { ++ DWC_DEBUGPL(DBG_ANY, "EP%d IN NAK Interrupt\n", ++ epnum); ++ handle_in_ep_nak_intr(pcd, epnum); ++ ++ CLEAR_IN_EP_INTR(core_if, epnum, nak); ++ } ++ } ++ epnum++; ++ ep_intr >>= 1; ++ } ++ ++ return 1; ++#undef CLEAR_IN_EP_INTR ++} ++ ++/** ++ * This interrupt indicates that an OUT EP has a pending Interrupt. ++ * The sequence for handling the OUT EP interrupt is shown below: ++ * -# Read the Device All Endpoint Interrupt register ++ * -# Repeat the following for each OUT EP interrupt bit set (from ++ * LSB to MSB). ++ * -# Read the Device Endpoint Interrupt (DOEPINTn) register ++ * -# If "Transfer Complete" call the request complete function ++ * -# If "Endpoint Disabled" complete the EP disable procedure. ++ * -# If "AHB Error Interrupt" log error ++ * -# If "Setup Phase Done" process Setup Packet (See Standard USB ++ * Command Processing) ++ */ ++static int32_t dwc_otg_pcd_handle_out_ep_intr(dwc_otg_pcd_t * pcd) ++{ ++#define CLEAR_OUT_EP_INTR(__core_if,__epnum,__intr) \ ++do { \ ++ doepint_data_t doepint = {.d32=0}; \ ++ doepint.b.__intr = 1; \ ++ dwc_write_reg32(&__core_if->dev_if->out_ep_regs[__epnum]->doepint, \ ++ doepint.d32); \ ++} while (0) ++ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++ dwc_otg_dev_if_t *dev_if = core_if->dev_if; ++ uint32_t ep_intr; ++ doepint_data_t doepint = {.d32 = 0 }; ++ dctl_data_t dctl = {.d32 = 0 }; ++ depctl_data_t doepctl = {.d32 = 0 }; ++ uint32_t epnum = 0; ++ dwc_otg_pcd_ep_t *ep; ++ dwc_ep_t *dwc_ep; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s()\n", __func__); ++ ++ /* Read in the device interrupt bits */ ++ ep_intr = dwc_otg_read_dev_all_out_ep_intr(core_if); ++ ++ while (ep_intr) { ++ if (ep_intr & 0x1) { ++ /* Get EP pointer */ ++ ep = get_out_ep(pcd, epnum); ++ dwc_ep = &ep->dwc_ep; ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, ++ "EP%d-%s: type=%d, mps=%d\n", ++ dwc_ep->num, (dwc_ep->is_in ? "IN" : "OUT"), ++ dwc_ep->type, dwc_ep->maxpacket); ++#endif ++ doepint.d32 = ++ dwc_otg_read_dev_out_ep_intr(core_if, dwc_ep); ++ ++ /* Transfer complete */ ++ if (doepint.b.xfercompl) { ++ ++ if (epnum == 0) { ++ /* Clear the bit in DOEPINTn for this interrupt */ ++ CLEAR_OUT_EP_INTR(core_if, epnum, ++ xfercompl); ++ if (core_if->dma_desc_enable == 0 ++ || pcd->ep0state != EP0_IDLE) ++ handle_ep0(pcd); ++#ifdef DWC_EN_ISOC ++ } else if (dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ if (doepint.b.pktdrpsts == 0) { ++ /* Clear the bit in DOEPINTn for this interrupt */ ++ CLEAR_OUT_EP_INTR(core_if, ++ epnum, ++ xfercompl); ++ complete_iso_ep(pcd, ep); ++ } else { ++ ++ doepint_data_t doepint = {.d32 = ++ 0 }; ++ doepint.b.xfercompl = 1; ++ doepint.b.pktdrpsts = 1; ++ dwc_write_reg32(&core_if-> ++ dev_if-> ++ out_ep_regs ++ [epnum]-> ++ doepint, ++ doepint.d32); ++ if (handle_iso_out_pkt_dropped ++ (core_if, dwc_ep)) { ++ complete_iso_ep(pcd, ++ ep); ++ } ++ } ++#endif /* DWC_EN_ISOC */ ++ } else { ++ /* Clear the bit in DOEPINTn for this interrupt */ ++ CLEAR_OUT_EP_INTR(core_if, epnum, ++ xfercompl); ++ complete_ep(ep); ++ } ++ ++ } ++ ++ /* Endpoint disable */ ++ if (doepint.b.epdisabled) { ++ ++ /* Clear the bit in DOEPINTn for this interrupt */ ++ CLEAR_OUT_EP_INTR(core_if, epnum, epdisabled); ++ } ++ /* AHB Error */ ++ if (doepint.b.ahberr) { ++ DWC_DEBUGPL(DBG_PCD, "EP%d OUT AHB Error\n", ++ epnum); ++ DWC_DEBUGPL(DBG_PCD, "EP DMA REG %d \n", ++ core_if->dev_if-> ++ out_ep_regs[epnum]->doepdma); ++ CLEAR_OUT_EP_INTR(core_if, epnum, ahberr); ++ } ++ /* Setup Phase Done (contorl EPs) */ ++ if (doepint.b.setup) { ++#ifdef DEBUG_EP0 ++ DWC_DEBUGPL(DBG_PCD, "EP%d SETUP Done\n", ++ epnum); ++#endif ++ CLEAR_OUT_EP_INTR(core_if, epnum, setup); ++ ++ handle_ep0(pcd); ++ } ++ ++ /** OUT EP BNA Intr */ ++ if (doepint.b.bna) { ++ CLEAR_OUT_EP_INTR(core_if, epnum, bna); ++ if (core_if->dma_desc_enable) { ++#ifdef DWC_EN_ISOC ++ if (dwc_ep->type == ++ DWC_OTG_EP_TYPE_ISOC) { ++ /* ++ * This checking is performed to prevent first "false" BNA ++ * handling occuring right after reconnect ++ */ ++ if (dwc_ep->next_frame != ++ 0xffffffff) ++ dwc_otg_pcd_handle_iso_bna ++ (ep); ++ } else ++#endif /* DWC_EN_ISOC */ ++ { ++ dctl.d32 = ++ dwc_read_reg32(&dev_if-> ++ dev_global_regs-> ++ dctl); ++ ++ /* If Global Continue on BNA is disabled - disable EP */ ++ if (!dctl.b.gcontbna) { ++ doepctl.d32 = 0; ++ doepctl.b.snak = 1; ++ doepctl.b.epdis = 1; ++ dwc_modify_reg32 ++ (&dev_if-> ++ out_ep_regs ++ [epnum]->doepctl, ++ doepctl.d32, ++ doepctl.d32); ++ } else { ++ start_next_request(ep); ++ } ++ } ++ } ++ } ++ if (doepint.b.stsphsercvd) { ++ CLEAR_OUT_EP_INTR(core_if, epnum, stsphsercvd); ++ if (core_if->dma_desc_enable) { ++ do_setup_in_status_phase(pcd); ++ } ++ } ++ /* Babble Interrutp */ ++ if (doepint.b.babble) { ++ DWC_DEBUGPL(DBG_ANY, "EP%d OUT Babble\n", ++ epnum); ++ handle_out_ep_babble_intr(pcd, epnum); ++ ++ CLEAR_OUT_EP_INTR(core_if, epnum, babble); ++ } ++ /* NAK Interrutp */ ++ if (doepint.b.nak) { ++ DWC_DEBUGPL(DBG_ANY, "EP%d OUT NAK\n", epnum); ++ handle_out_ep_nak_intr(pcd, epnum); ++ ++ CLEAR_OUT_EP_INTR(core_if, epnum, nak); ++ } ++ /* NYET Interrutp */ ++ if (doepint.b.nyet) { ++ DWC_DEBUGPL(DBG_ANY, "EP%d OUT NYET\n", epnum); ++ handle_out_ep_nyet_intr(pcd, epnum); ++ ++ CLEAR_OUT_EP_INTR(core_if, epnum, nyet); ++ } ++ } ++ ++ epnum++; ++ ep_intr >>= 1; ++ } ++ ++ return 1; ++ ++#undef CLEAR_OUT_EP_INTR ++} ++ ++/** ++ * Incomplete ISO IN Transfer Interrupt. ++ * This interrupt indicates one of the following conditions occurred ++ * while transmitting an ISOC transaction. ++ * - Corrupted IN Token for ISOC EP. ++ * - Packet not complete in FIFO. ++ * The follow actions will be taken: ++ * -# Determine the EP ++ * -# Set incomplete flag in dwc_ep structure ++ * -# Disable EP; when "Endpoint Disabled" interrupt is received ++ * Flush FIFO ++ */ ++int32_t dwc_otg_pcd_handle_incomplete_isoc_in_intr(dwc_otg_pcd_t * pcd) ++{ ++ gintsts_data_t gintsts; ++ ++#ifdef DWC_EN_ISOC ++ dwc_otg_dev_if_t *dev_if; ++ deptsiz_data_t deptsiz = {.d32 = 0 }; ++ depctl_data_t depctl = {.d32 = 0 }; ++ dsts_data_t dsts = {.d32 = 0 }; ++ dwc_ep_t *dwc_ep; ++ int i; ++ ++ dev_if = GET_CORE_IF(pcd)->dev_if; ++ ++ for (i = 1; i <= dev_if->num_in_eps; ++i) { ++ dwc_ep = &pcd->in_ep[i].dwc_ep; ++ if (dwc_ep->active && dwc_ep->type == DWC_OTG_EP_TYPE_ISOC) { ++ deptsiz.d32 = ++ dwc_read_reg32(&dev_if->in_ep_regs[i]->dieptsiz); ++ depctl.d32 = ++ dwc_read_reg32(&dev_if->in_ep_regs[i]->diepctl); ++ ++ if (depctl.b.epdis && deptsiz.d32) { ++ set_current_pkt_info(GET_CORE_IF(pcd), dwc_ep); ++ if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { ++ dwc_ep->cur_pkt = 0; ++ dwc_ep->proc_buf_num = ++ (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ ++ if (dwc_ep->proc_buf_num) { ++ dwc_ep->cur_pkt_addr = ++ dwc_ep->xfer_buff1; ++ dwc_ep->cur_pkt_dma_addr = ++ dwc_ep->dma_addr1; ++ } else { ++ dwc_ep->cur_pkt_addr = ++ dwc_ep->xfer_buff0; ++ dwc_ep->cur_pkt_dma_addr = ++ dwc_ep->dma_addr0; ++ } ++ ++ } ++ ++ dsts.d32 = ++ dwc_read_reg32(&GET_CORE_IF(pcd)->dev_if-> ++ dev_global_regs->dsts); ++ dwc_ep->next_frame = dsts.b.soffn; ++ ++ dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF ++ (pcd), ++ dwc_ep); ++ } ++ } ++ } ++ ++#else ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", ++ "IN ISOC Incomplete"); ++ ++ intr_mask.b.incomplisoin = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++#endif //DWC_EN_ISOC ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.incomplisoin = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * Incomplete ISO OUT Transfer Interrupt. ++ * ++ * This interrupt indicates that the core has dropped an ISO OUT ++ * packet. The following conditions can be the cause: ++ * - FIFO Full, the entire packet would not fit in the FIFO. ++ * - CRC Error ++ * - Corrupted Token ++ * The follow actions will be taken: ++ * -# Determine the EP ++ * -# Set incomplete flag in dwc_ep structure ++ * -# Read any data from the FIFO ++ * -# Disable EP. when "Endpoint Disabled" interrupt is received ++ * re-enable EP. ++ */ ++int32_t dwc_otg_pcd_handle_incomplete_isoc_out_intr(dwc_otg_pcd_t * pcd) ++{ ++ ++ gintsts_data_t gintsts; ++ ++#ifdef DWC_EN_ISOC ++ dwc_otg_dev_if_t *dev_if; ++ deptsiz_data_t deptsiz = {.d32 = 0 }; ++ depctl_data_t depctl = {.d32 = 0 }; ++ dsts_data_t dsts = {.d32 = 0 }; ++ dwc_ep_t *dwc_ep; ++ int i; ++ ++ dev_if = GET_CORE_IF(pcd)->dev_if; ++ ++ for (i = 1; i <= dev_if->num_out_eps; ++i) { ++ dwc_ep = &pcd->in_ep[i].dwc_ep; ++ if (pcd->out_ep[i].dwc_ep.active && ++ pcd->out_ep[i].dwc_ep.type == DWC_OTG_EP_TYPE_ISOC) { ++ deptsiz.d32 = ++ dwc_read_reg32(&dev_if->out_ep_regs[i]->doeptsiz); ++ depctl.d32 = ++ dwc_read_reg32(&dev_if->out_ep_regs[i]->doepctl); ++ ++ if (depctl.b.epdis && deptsiz.d32) { ++ set_current_pkt_info(GET_CORE_IF(pcd), ++ &pcd->out_ep[i].dwc_ep); ++ if (dwc_ep->cur_pkt >= dwc_ep->pkt_cnt) { ++ dwc_ep->cur_pkt = 0; ++ dwc_ep->proc_buf_num = ++ (dwc_ep->proc_buf_num ^ 1) & 0x1; ++ ++ if (dwc_ep->proc_buf_num) { ++ dwc_ep->cur_pkt_addr = ++ dwc_ep->xfer_buff1; ++ dwc_ep->cur_pkt_dma_addr = ++ dwc_ep->dma_addr1; ++ } else { ++ dwc_ep->cur_pkt_addr = ++ dwc_ep->xfer_buff0; ++ dwc_ep->cur_pkt_dma_addr = ++ dwc_ep->dma_addr0; ++ } ++ ++ } ++ ++ dsts.d32 = ++ dwc_read_reg32(&GET_CORE_IF(pcd)->dev_if-> ++ dev_global_regs->dsts); ++ dwc_ep->next_frame = dsts.b.soffn; ++ ++ dwc_otg_iso_ep_start_frm_transfer(GET_CORE_IF ++ (pcd), ++ dwc_ep); ++ } ++ } ++ } ++#else ++ /** @todo implement ISR */ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", ++ "OUT ISOC Incomplete"); ++ ++ intr_mask.b.incomplisoout = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++#endif /* DWC_EN_ISOC */ ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.incomplisoout = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * This function handles the Global IN NAK Effective interrupt. ++ * ++ */ ++int32_t dwc_otg_pcd_handle_in_nak_effective(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_dev_if_t *dev_if = GET_CORE_IF(pcd)->dev_if; ++ depctl_data_t diepctl = {.d32 = 0 }; ++ depctl_data_t diepctl_rd = {.d32 = 0 }; ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ gintsts_data_t gintsts; ++ int i; ++ ++ DWC_DEBUGPL(DBG_PCD, "Global IN NAK Effective\n"); ++ ++ /* Disable all active IN EPs */ ++ diepctl.b.epdis = 1; ++ diepctl.b.snak = 1; ++ ++ for (i = 0; i <= dev_if->num_in_eps; i++) { ++ diepctl_rd.d32 = ++ dwc_read_reg32(&dev_if->in_ep_regs[i]->diepctl); ++ if (diepctl_rd.b.epena) { ++ dwc_write_reg32(&dev_if->in_ep_regs[i]->diepctl, ++ diepctl.d32); ++ } ++ } ++ /* Disable the Global IN NAK Effective Interrupt */ ++ intr_mask.b.ginnakeff = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.ginnakeff = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * OUT NAK Effective. ++ * ++ */ ++int32_t dwc_otg_pcd_handle_out_nak_effective(dwc_otg_pcd_t * pcd) ++{ ++ gintmsk_data_t intr_mask = {.d32 = 0 }; ++ gintsts_data_t gintsts; ++ ++ DWC_PRINTF("INTERRUPT Handler not implemented for %s\n", ++ "Global IN NAK Effective\n"); ++ /* Disable the Global IN NAK Effective Interrupt */ ++ intr_mask.b.goutnakeff = 1; ++ dwc_modify_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintmsk, ++ intr_mask.d32, 0); ++ ++ /* Clear interrupt */ ++ gintsts.d32 = 0; ++ gintsts.b.goutnakeff = 1; ++ dwc_write_reg32(&GET_CORE_IF(pcd)->core_global_regs->gintsts, ++ gintsts.d32); ++ ++ return 1; ++} ++ ++/** ++ * PCD interrupt handler. ++ * ++ * The PCD handles the device interrupts. Many conditions can cause a ++ * device interrupt. When an interrupt occurs, the device interrupt ++ * service routine determines the cause of the interrupt and ++ * dispatches handling to the appropriate function. These interrupt ++ * handling functions are described below. ++ * ++ * All interrupt registers are processed from LSB to MSB. ++ * ++ */ ++int32_t dwc_otg_pcd_handle_intr(dwc_otg_pcd_t * pcd) ++{ ++ dwc_otg_core_if_t *core_if = GET_CORE_IF(pcd); ++#ifdef VERBOSE ++ dwc_otg_core_global_regs_t *global_regs = core_if->core_global_regs; ++#endif ++ gintsts_data_t gintr_status; ++ int32_t retval = 0; ++ ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_ANY, "%s() gintsts=%08x gintmsk=%08x\n", ++ __func__, ++ dwc_read_reg32(&global_regs->gintsts), ++ dwc_read_reg32(&global_regs->gintmsk)); ++#endif ++ ++ if (dwc_otg_is_device_mode(core_if)) { ++ DWC_SPINLOCK(pcd->lock); ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%08x gintmsk=%08x\n", ++ __func__, ++ dwc_read_reg32(&global_regs->gintsts), ++ dwc_read_reg32(&global_regs->gintmsk)); ++#endif ++ ++ gintr_status.d32 = dwc_otg_read_core_intr(core_if); ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s: gintsts&gintmsk=%08x\n", ++ __func__, gintr_status.d32); ++ ++ if (gintr_status.b.sofintr) { ++ retval |= dwc_otg_pcd_handle_sof_intr(pcd); ++ } ++ if (gintr_status.b.rxstsqlvl) { ++ retval |= ++ dwc_otg_pcd_handle_rx_status_q_level_intr(pcd); ++ } ++ if (gintr_status.b.nptxfempty) { ++ retval |= dwc_otg_pcd_handle_np_tx_fifo_empty_intr(pcd); ++ } ++ if (gintr_status.b.ginnakeff) { ++ retval |= dwc_otg_pcd_handle_in_nak_effective(pcd); ++ } ++ if (gintr_status.b.goutnakeff) { ++ retval |= dwc_otg_pcd_handle_out_nak_effective(pcd); ++ } ++ if (gintr_status.b.i2cintr) { ++ retval |= dwc_otg_pcd_handle_i2c_intr(pcd); ++ } ++ if (gintr_status.b.erlysuspend) { ++ retval |= dwc_otg_pcd_handle_early_suspend_intr(pcd); ++ } ++ if (gintr_status.b.usbreset) { ++ retval |= dwc_otg_pcd_handle_usb_reset_intr(pcd); ++ } ++ if (gintr_status.b.enumdone) { ++ retval |= dwc_otg_pcd_handle_enum_done_intr(pcd); ++ } ++ if (gintr_status.b.isooutdrop) { ++ retval |= ++ dwc_otg_pcd_handle_isoc_out_packet_dropped_intr ++ (pcd); ++ } ++ if (gintr_status.b.eopframe) { ++ retval |= ++ dwc_otg_pcd_handle_end_periodic_frame_intr(pcd); ++ } ++ if (gintr_status.b.epmismatch) { ++ retval |= dwc_otg_pcd_handle_ep_mismatch_intr(core_if); ++ } ++ if (gintr_status.b.inepint) { ++ if (!core_if->multiproc_int_enable) { ++ retval |= dwc_otg_pcd_handle_in_ep_intr(pcd); ++ } ++ } ++ if (gintr_status.b.outepintr) { ++ if (!core_if->multiproc_int_enable) { ++ retval |= dwc_otg_pcd_handle_out_ep_intr(pcd); ++ } ++ } ++ if (gintr_status.b.incomplisoin) { ++ retval |= ++ dwc_otg_pcd_handle_incomplete_isoc_in_intr(pcd); ++ } ++ if (gintr_status.b.incomplisoout) { ++ retval |= ++ dwc_otg_pcd_handle_incomplete_isoc_out_intr(pcd); ++ } ++ ++ /* In MPI mode De vice Endpoints intterrupts are asserted ++ * without setting outepintr and inepint bits set, so these ++ * Interrupt handlers are called without checking these bit-fields ++ */ ++ if (core_if->multiproc_int_enable) { ++ retval |= dwc_otg_pcd_handle_in_ep_intr(pcd); ++ retval |= dwc_otg_pcd_handle_out_ep_intr(pcd); ++ } ++#ifdef VERBOSE ++ DWC_DEBUGPL(DBG_PCDV, "%s() gintsts=%0x\n", __func__, ++ dwc_read_reg32(&global_regs->gintsts)); ++#endif ++ DWC_SPINUNLOCK(pcd->lock); ++ } ++ return retval; ++} ++ ++#endif /* DWC_HOST_ONLY */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_pcd_linux.c +@@ -0,0 +1,1288 @@ ++ /* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_pcd_linux.c $ ++ * $Revision: #7 $ ++ * $Date: 2009/04/03 $ ++ * $Change: 1225160 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++#ifndef DWC_HOST_ONLY ++ ++/** @file ++ * This file implements the Peripheral Controller Driver. ++ * ++ * The Peripheral Controller Driver (PCD) is responsible for ++ * translating requests from the Function Driver into the appropriate ++ * actions on the DWC_otg controller. It isolates the Function Driver ++ * from the specifics of the controller by providing an API to the ++ * Function Driver. ++ * ++ * The Peripheral Controller Driver for Linux will implement the ++ * Gadget API, so that the existing Gadget drivers can be used. ++ * (Gadget Driver is the Linux terminology for a Function Driver.) ++ * ++ * The Linux Gadget API is defined in the header file ++ * <code><linux/usb_gadget.h></code>. The USB EP operations API is ++ * defined in the structure <code>usb_ep_ops</code> and the USB ++ * Controller API is defined in the structure ++ * <code>usb_gadget_ops</code>. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/errno.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/string.h> ++#include <linux/dma-mapping.h> ++#include <linux/version.h> ++ ++#if defined(LM_INTERFACE) ++//# include <asm/arch/regs-irq.h> ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++#include <asm/arch/lm.h> ++#else ++/* by 2.6.31, at least, the location of some headers has changed ++*/ ++#include <mach/lm.h> ++#endif ++ ++#elif defined(PLATFORM_INTERFACE) ++#include <linux/platform_device.h> ++#endif ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++#include <asm/arch/irqs.h> ++#include <linux/usb_ch9.h> ++#include <linux/usb_gadget.h> ++#else ++/* by 2.6.31, at least, the location of some headers has changed ++*/ ++#include <mach/irqs.h> ++#include <linux/usb/ch9.h> ++#include <linux/usb/gadget.h> ++#endif ++ ++#include <asm/io.h> ++ ++#include "dwc_otg_pcd_if.h" ++#include "dwc_otg_driver.h" ++#include "dwc_otg_dbg.h" ++ ++static struct gadget_wrapper { ++ dwc_otg_pcd_t *pcd; ++ ++ struct usb_gadget gadget; ++ struct usb_gadget_driver *driver; ++ ++ struct usb_ep ep0; ++ struct usb_ep in_ep[16]; ++ struct usb_ep out_ep[16]; ++ ++} *gadget_wrapper; ++ ++/* Display the contents of the buffer */ ++extern void dump_msg(const u8 * buf, unsigned int length); ++ ++/* USB Endpoint Operations */ ++/* ++ * The following sections briefly describe the behavior of the Gadget ++ * API endpoint operations implemented in the DWC_otg driver ++ * software. Detailed descriptions of the generic behavior of each of ++ * these functions can be found in the Linux header file ++ * include/linux/usb_gadget.h. ++ * ++ * The Gadget API provides wrapper functions for each of the function ++ * pointers defined in usb_ep_ops. The Gadget Driver calls the wrapper ++ * function, which then calls the underlying PCD function. The ++ * following sections are named according to the wrapper ++ * functions. Within each section, the corresponding DWC_otg PCD ++ * function name is specified. ++ * ++ */ ++ ++/** ++ * This function is called by the Gadget Driver for each EP to be ++ * configured for the current configuration (SET_CONFIGURATION). ++ * ++ * This function initializes the dwc_otg_ep_t data structure, and then ++ * calls dwc_otg_ep_activate. ++ */ ++static int ep_enable(struct usb_ep *usb_ep, ++ const struct usb_endpoint_descriptor *ep_desc) ++{ ++ int retval; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, ep_desc); ++ ++ if (!usb_ep || !ep_desc || ep_desc->bDescriptorType != USB_DT_ENDPOINT) { ++ DWC_WARN("%s, bad ep or descriptor\n", __func__); ++ return -EINVAL; ++ } ++ if (usb_ep == &gadget_wrapper->ep0) { ++ DWC_WARN("%s, bad ep(0)\n", __func__); ++ return -EINVAL; ++ } ++ ++ /* Check FIFO size? */ ++ if (!ep_desc->wMaxPacketSize) { ++ DWC_WARN("%s, bad %s maxpacket\n", __func__, usb_ep->name); ++ return -ERANGE; ++ } ++ ++ if (!gadget_wrapper->driver || ++ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { ++ DWC_WARN("%s, bogus device state\n", __func__); ++ return -ESHUTDOWN; ++ } ++ ++ retval = dwc_otg_pcd_ep_enable(gadget_wrapper->pcd, ++ (const uint8_t *)ep_desc, ++ (void *)usb_ep); ++ if (retval) { ++ DWC_WARN("dwc_otg_pcd_ep_enable failed\n"); ++ return -EINVAL; ++ } ++ ++ usb_ep->maxpacket = le16_to_cpu(ep_desc->wMaxPacketSize); ++ ++ return 0; ++} ++ ++/** ++ * This function is called when an EP is disabled due to disconnect or ++ * change in configuration. Any pending requests will terminate with a ++ * status of -ESHUTDOWN. ++ * ++ * This function modifies the dwc_otg_ep_t data structure for this EP, ++ * and then calls dwc_otg_ep_deactivate. ++ */ ++static int ep_disable(struct usb_ep *usb_ep) ++{ ++ int retval; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, usb_ep); ++ if (!usb_ep) { ++ DWC_DEBUGPL(DBG_PCD, "%s, %s not enabled\n", __func__, ++ usb_ep ? usb_ep->name : NULL); ++ return -EINVAL; ++ } ++ ++ retval = dwc_otg_pcd_ep_disable(gadget_wrapper->pcd, usb_ep); ++ if (retval) { ++ retval = -EINVAL; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function allocates a request object to use with the specified ++ * endpoint. ++ * ++ * @param ep The endpoint to be used with with the request ++ * @param gfp_flags the GFP_* flags to use. ++ */ ++static struct usb_request *dwc_otg_pcd_alloc_request(struct usb_ep *ep, ++ gfp_t gfp_flags) ++{ ++ struct usb_request *usb_req; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d)\n", __func__, ep, gfp_flags); ++ if (0 == ep) { ++ DWC_WARN("%s() %s\n", __func__, "Invalid EP!\n"); ++ return 0; ++ } ++ usb_req = kmalloc(sizeof(*usb_req), gfp_flags); ++ if (0 == usb_req) { ++ DWC_WARN("%s() %s\n", __func__, "request allocation failed!\n"); ++ return 0; ++ } ++ memset(usb_req, 0, sizeof(*usb_req)); ++ usb_req->dma = DWC_INVALID_DMA_ADDR; ++ ++ return usb_req; ++} ++ ++/** ++ * This function frees a request object. ++ * ++ * @param ep The endpoint associated with the request ++ * @param req The request being freed ++ */ ++static void dwc_otg_pcd_free_request(struct usb_ep *ep, struct usb_request *req) ++{ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, ep, req); ++ ++ if (0 == ep || 0 == req) { ++ DWC_WARN("%s() %s\n", __func__, ++ "Invalid ep or req argument!\n"); ++ return; ++ } ++ ++ kfree(req); ++} ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++/** ++ * This function allocates an I/O buffer to be used for a transfer ++ * to/from the specified endpoint. ++ * ++ * @param usb_ep The endpoint to be used with with the request ++ * @param bytes The desired number of bytes for the buffer ++ * @param dma Pointer to the buffer's DMA address; must be valid ++ * @param gfp_flags the GFP_* flags to use. ++ * @return address of a new buffer or null is buffer could not be allocated. ++ */ ++static void *dwc_otg_pcd_alloc_buffer(struct usb_ep *usb_ep, unsigned bytes, ++ dma_addr_t * dma, gfp_t gfp_flags) ++{ ++ void *buf; ++ dwc_otg_pcd_t *pcd = 0; ++ ++ pcd = gadget_wrapper->pcd; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%d,%p,%0x)\n", __func__, usb_ep, bytes, ++ dma, gfp_flags); ++ ++ /* Check dword alignment */ ++ if ((bytes & 0x3UL) != 0) { ++ DWC_WARN("%s() Buffer size is not a multiple of" ++ "DWORD size (%d)", __func__, bytes); ++ } ++ ++ buf = dma_alloc_coherent(NULL, bytes, dma, gfp_flags); ++ ++ /* Check dword alignment */ ++ if (((int)buf & 0x3UL) != 0) { ++ DWC_WARN("%s() Buffer is not DWORD aligned (%p)", ++ __func__, buf); ++ } ++ ++ return buf; ++} ++ ++/** ++ * This function frees an I/O buffer that was allocated by alloc_buffer. ++ * ++ * @param usb_ep the endpoint associated with the buffer ++ * @param buf address of the buffer ++ * @param dma The buffer's DMA address ++ * @param bytes The number of bytes of the buffer ++ */ ++static void dwc_otg_pcd_free_buffer(struct usb_ep *usb_ep, void *buf, ++ dma_addr_t dma, unsigned bytes) ++{ ++ dwc_otg_pcd_t *pcd = 0; ++ ++ pcd = gadget_wrapper->pcd; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%0x,%d)\n", __func__, buf, dma, bytes); ++ ++ dma_free_coherent(NULL, bytes, buf, dma); ++} ++#endif ++ ++/** ++ * This function is used to submit an I/O Request to an EP. ++ * ++ * - When the request completes the request's completion callback ++ * is called to return the request to the driver. ++ * - An EP, except control EPs, may have multiple requests ++ * pending. ++ * - Once submitted the request cannot be examined or modified. ++ * - Each request is turned into one or more packets. ++ * - A BULK EP can queue any amount of data; the transfer is ++ * packetized. ++ * - Zero length Packets are specified with the request 'zero' ++ * flag. ++ */ ++static int ep_queue(struct usb_ep *usb_ep, struct usb_request *usb_req, ++ gfp_t gfp_flags) ++{ ++ dwc_otg_pcd_t *pcd; ++ int retval; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p,%d)\n", ++ __func__, usb_ep, usb_req, gfp_flags); ++ ++ if (!usb_req || !usb_req->complete || !usb_req->buf) { ++ DWC_WARN("bad params\n"); ++ return -EINVAL; ++ } ++ ++ if (!usb_ep) { ++ DWC_WARN("bad ep\n"); ++ return -EINVAL; ++ } ++ ++ pcd = gadget_wrapper->pcd; ++ if (!gadget_wrapper->driver || ++ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { ++ DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", ++ gadget_wrapper->gadget.speed); ++ DWC_WARN("bogus device state\n"); ++ return -ESHUTDOWN; ++ } ++ ++ DWC_DEBUGPL(DBG_PCD, "%s queue req %p, len %d buf %p\n", ++ usb_ep->name, usb_req, usb_req->length, usb_req->buf); ++ ++ usb_req->status = -EINPROGRESS; ++ usb_req->actual = 0; ++ ++ retval = dwc_otg_pcd_ep_queue(pcd, usb_ep, usb_req->buf, usb_req->dma, ++ usb_req->length, usb_req->zero, usb_req, ++ gfp_flags == GFP_ATOMIC ? 1 : 0); ++ if (retval) { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * This function cancels an I/O request from an EP. ++ */ ++static int ep_dequeue(struct usb_ep *usb_ep, struct usb_request *usb_req) ++{ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p,%p)\n", __func__, usb_ep, usb_req); ++ ++ if (!usb_ep || !usb_req) { ++ DWC_WARN("bad argument\n"); ++ return -EINVAL; ++ } ++ if (!gadget_wrapper->driver || ++ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { ++ DWC_WARN("bogus device state\n"); ++ return -ESHUTDOWN; ++ } ++ if (dwc_otg_pcd_ep_dequeue(gadget_wrapper->pcd, usb_ep, usb_req)) { ++ return -EINVAL; ++ } ++ ++ return 0; ++} ++ ++/** ++ * usb_ep_set_halt stalls an endpoint. ++ * ++ * usb_ep_clear_halt clears an endpoint halt and resets its data ++ * toggle. ++ * ++ * Both of these functions are implemented with the same underlying ++ * function. The behavior depends on the value argument. ++ * ++ * @param[in] usb_ep the Endpoint to halt or clear halt. ++ * @param[in] value ++ * - 0 means clear_halt. ++ * - 1 means set_halt, ++ * - 2 means clear stall lock flag. ++ * - 3 means set stall lock flag. ++ */ ++static int ep_halt(struct usb_ep *usb_ep, int value) ++{ ++ int retval = 0; ++ ++ DWC_DEBUGPL(DBG_PCD, "HALT %s %d\n", usb_ep->name, value); ++ ++ if (!usb_ep) { ++ DWC_WARN("bad ep\n"); ++ return -EINVAL; ++ } ++ ++ retval = dwc_otg_pcd_ep_halt(gadget_wrapper->pcd, usb_ep, value); ++ if (retval == -DWC_E_AGAIN) { ++ return -EAGAIN; ++ } else if (retval) { ++ retval = -EINVAL; ++ } ++ ++ return retval; ++} ++ ++//#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,30)) ++#if 0 ++/** ++ * ep_wedge: sets the halt feature and ignores clear requests ++ * ++ * @usb_ep: the endpoint being wedged ++ * ++ * Use this to stall an endpoint and ignore CLEAR_FEATURE(HALT_ENDPOINT) ++ * requests. If the gadget driver clears the halt status, it will ++ * automatically unwedge the endpoint. ++ * ++ * Returns zero on success, else negative errno. * ++ * Check usb_ep_set_wedge() at "usb_gadget.h" for details ++ */ ++static int ep_wedge(struct usb_ep *usb_ep) ++{ ++ int retval = 0; ++ ++ DWC_DEBUGPL(DBG_PCD, "WEDGE %s\n", usb_ep->name); ++ ++ if (!usb_ep) { ++ DWC_WARN("bad ep\n"); ++ return -EINVAL; ++ } ++ ++ retval = dwc_otg_pcd_ep_wedge(gadget_wrapper->pcd, usb_ep); ++ if (retval == -DWC_E_AGAIN) { ++ retval = -EAGAIN; ++ } else if (retval) { ++ retval = -EINVAL; ++ } ++ ++ return retval; ++} ++#endif ++ ++#ifdef DWC_EN_ISOC ++/** ++ * This function is used to submit an ISOC Transfer Request to an EP. ++ * ++ * - Every time a sync period completes the request's completion callback ++ * is called to provide data to the gadget driver. ++ * - Once submitted the request cannot be modified. ++ * - Each request is turned into periodic data packets untill ISO ++ * Transfer is stopped.. ++ */ ++static int iso_ep_start(struct usb_ep *usb_ep, struct usb_iso_request *req, ++ gfp_t gfp_flags) ++{ ++ int retval = 0; ++ ++ if (!req || !req->process_buffer || !req->buf0 || !req->buf1) { ++ DWC_WARN("bad params\n"); ++ return -EINVAL; ++ } ++ ++ if (!usb_ep) { ++ DWC_PRINTF("bad params\n"); ++ return -EINVAL; ++ } ++ ++ req->status = -EINPROGRESS; ++ ++ retval = ++ dwc_otg_pcd_iso_ep_start(gadget_wrapper->pcd, usb_ep, req->buf0, ++ req->buf1, req->dma0, req->dma1, ++ req->sync_frame, req->data_pattern_frame, ++ req->data_per_frame, ++ req->flags & USB_REQ_ISO_ASAP ? -1 : req-> ++ start_frame, req->buf_proc_intrvl, req, ++ gfp_flags == GFP_ATOMIC ? 1 : 0); ++ ++ if (retval) { ++ return -EINVAL; ++ } ++ ++ return retval; ++} ++ ++/** ++ * This function stops ISO EP Periodic Data Transfer. ++ */ ++static int iso_ep_stop(struct usb_ep *usb_ep, struct usb_iso_request *req) ++{ ++ int retval = 0; ++ if (!usb_ep) { ++ DWC_WARN("bad ep\n"); ++ } ++ ++ if (!gadget_wrapper->driver || ++ gadget_wrapper->gadget.speed == USB_SPEED_UNKNOWN) { ++ DWC_DEBUGPL(DBG_PCDV, "gadget.speed=%d\n", ++ gadget_wrapper->gadget.speed); ++ DWC_WARN("bogus device state\n"); ++ } ++ ++ dwc_otg_pcd_iso_ep_stop(gadget_wrapper->pcd, usb_ep, req); ++ if (retval) { ++ retval = -EINVAL; ++ } ++ ++ return retval; ++} ++ ++static struct usb_iso_request *alloc_iso_request(struct usb_ep *ep, ++ int packets, gfp_t gfp_flags) ++{ ++ struct usb_iso_request *pReq = NULL; ++ uint32_t req_size; ++ ++ req_size = sizeof(struct usb_iso_request); ++ req_size += ++ (2 * packets * (sizeof(struct usb_gadget_iso_packet_descriptor))); ++ ++ pReq = kmalloc(req_size, gfp_flags); ++ if (!pReq) { ++ DWC_WARN("Can't allocate Iso Request\n"); ++ return 0; ++ } ++ pReq->iso_packet_desc0 = (void *)(pReq + 1); ++ ++ pReq->iso_packet_desc1 = pReq->iso_packet_desc0 + packets; ++ ++ return pReq; ++} ++ ++static void free_iso_request(struct usb_ep *ep, struct usb_iso_request *req) ++{ ++ kfree(req); ++} ++ ++static struct usb_isoc_ep_ops dwc_otg_pcd_ep_ops = { ++ .ep_ops = { ++ .enable = ep_enable, ++ .disable = ep_disable, ++ ++ .alloc_request = dwc_otg_pcd_alloc_request, ++ .free_request = dwc_otg_pcd_free_request, ++ ++ .alloc_buffer = dwc_otg_pcd_alloc_buffer, ++ .free_buffer = dwc_otg_pcd_free_buffer, ++ ++ .queue = ep_queue, ++ .dequeue = ep_dequeue, ++ ++ .set_halt = ep_halt, ++ .fifo_status = 0, ++ .fifo_flush = 0, ++ }, ++ .iso_ep_start = iso_ep_start, ++ .iso_ep_stop = iso_ep_stop, ++ .alloc_iso_request = alloc_iso_request, ++ .free_iso_request = free_iso_request, ++}; ++ ++#else ++ ++ int (*enable) (struct usb_ep *ep, ++ const struct usb_endpoint_descriptor *desc); ++ int (*disable) (struct usb_ep *ep); ++ ++ struct usb_request *(*alloc_request) (struct usb_ep *ep, ++ gfp_t gfp_flags); ++ void (*free_request) (struct usb_ep *ep, struct usb_request *req); ++ ++ int (*queue) (struct usb_ep *ep, struct usb_request *req, ++ gfp_t gfp_flags); ++ int (*dequeue) (struct usb_ep *ep, struct usb_request *req); ++ ++ int (*set_halt) (struct usb_ep *ep, int value); ++ int (*set_wedge) (struct usb_ep *ep); ++ ++ int (*fifo_status) (struct usb_ep *ep); ++ void (*fifo_flush) (struct usb_ep *ep); ++static struct usb_ep_ops dwc_otg_pcd_ep_ops = { ++ .enable = ep_enable, ++ .disable = ep_disable, ++ ++ .alloc_request = dwc_otg_pcd_alloc_request, ++ .free_request = dwc_otg_pcd_free_request, ++ ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++ .alloc_buffer = dwc_otg_pcd_alloc_buffer, ++ .free_buffer = dwc_otg_pcd_free_buffer, ++#else ++ /* .set_wedge = ep_wedge, */ ++ .set_wedge = NULL, /* uses set_halt instead */ ++#endif ++ ++ .queue = ep_queue, ++ .dequeue = ep_dequeue, ++ ++ .set_halt = ep_halt, ++ .fifo_status = 0, ++ .fifo_flush = 0, ++ ++}; ++ ++#endif /* _EN_ISOC_ */ ++/* Gadget Operations */ ++/** ++ * The following gadget operations will be implemented in the DWC_otg ++ * PCD. Functions in the API that are not described below are not ++ * implemented. ++ * ++ * The Gadget API provides wrapper functions for each of the function ++ * pointers defined in usb_gadget_ops. The Gadget Driver calls the ++ * wrapper function, which then calls the underlying PCD function. The ++ * following sections are named according to the wrapper functions ++ * (except for ioctl, which doesn't have a wrapper function). Within ++ * each section, the corresponding DWC_otg PCD function name is ++ * specified. ++ * ++ */ ++ ++/** ++ *Gets the USB Frame number of the last SOF. ++ */ ++static int get_frame_number(struct usb_gadget *gadget) ++{ ++ struct gadget_wrapper *d; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget); ++ ++ if (gadget == 0) { ++ return -ENODEV; ++ } ++ ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ return dwc_otg_pcd_get_frame_number(d->pcd); ++} ++ ++#ifdef CONFIG_USB_DWC_OTG_LPM ++static int test_lpm_enabled(struct usb_gadget *gadget) ++{ ++ struct gadget_wrapper *d; ++ ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ ++ return dwc_otg_pcd_is_lpm_enabled(d->pcd); ++} ++#endif ++ ++/** ++ * Initiates Session Request Protocol (SRP) to wakeup the host if no ++ * session is in progress. If a session is already in progress, but ++ * the device is suspended, remote wakeup signaling is started. ++ * ++ */ ++static int wakeup(struct usb_gadget *gadget) ++{ ++ struct gadget_wrapper *d; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, gadget); ++ ++ if (gadget == 0) { ++ return -ENODEV; ++ } else { ++ d = container_of(gadget, struct gadget_wrapper, gadget); ++ } ++ dwc_otg_pcd_wakeup(d->pcd); ++ return 0; ++} ++ ++static const struct usb_gadget_ops dwc_otg_pcd_ops = { ++ .get_frame = get_frame_number, ++ .wakeup = wakeup, ++#ifdef CONFIG_USB_DWC_OTG_LPM ++ .lpm_support = test_lpm_enabled, ++#endif ++ // current versions must always be self-powered ++}; ++ ++static int _setup(dwc_otg_pcd_t * pcd, uint8_t * bytes) ++{ ++ int retval = -DWC_E_NOT_SUPPORTED; ++ if (gadget_wrapper->driver && gadget_wrapper->driver->setup) { ++ retval = gadget_wrapper->driver->setup(&gadget_wrapper->gadget, ++ (struct usb_ctrlrequest ++ *)bytes); ++ } ++ ++ if (retval == -ENOTSUPP) { ++ retval = -DWC_E_NOT_SUPPORTED; ++ } else if (retval < 0) { ++ retval = -DWC_E_INVALID; ++ } ++ ++ return retval; ++} ++ ++#ifdef DWC_EN_ISOC ++static int _isoc_complete(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle, int proc_buf_num) ++{ ++ int i, packet_count; ++ struct usb_gadget_iso_packet_descriptor *iso_packet = 0; ++ struct usb_iso_request *iso_req = req_handle; ++ ++ if (proc_buf_num) { ++ iso_packet = iso_req->iso_packet_desc1; ++ } else { ++ iso_packet = iso_req->iso_packet_desc0; ++ } ++ packet_count = ++ dwc_otg_pcd_get_iso_packet_count(pcd, ep_handle, req_handle); ++ for (i = 0; i < packet_count; ++i) { ++ int status; ++ int actual; ++ int offset; ++ dwc_otg_pcd_get_iso_packet_params(pcd, ep_handle, req_handle, ++ i, &status, &actual, &offset); ++ switch (status) { ++ case -DWC_E_NO_DATA: ++ status = -ENODATA; ++ break; ++ default: ++ if (status) { ++ DWC_PRINTF("unknown status in isoc packet\n"); ++ } ++ ++ } ++ iso_packet[i].status = status; ++ iso_packet[i].offset = offset; ++ iso_packet[i].actual_length = actual; ++ } ++ ++ iso_req->status = 0; ++ iso_req->process_buffer(ep_handle, iso_req); ++ ++ return 0; ++} ++#endif /* DWC_EN_ISOC */ ++ ++static int _complete(dwc_otg_pcd_t * pcd, void *ep_handle, ++ void *req_handle, int32_t status, uint32_t actual) ++{ ++ struct usb_request *req = (struct usb_request *)req_handle; ++ ++ if (req && req->complete) { ++ switch (status) { ++ case -DWC_E_SHUTDOWN: ++ req->status = -ESHUTDOWN; ++ break; ++ case -DWC_E_RESTART: ++ req->status = -ECONNRESET; ++ break; ++ case -DWC_E_INVALID: ++ req->status = -EINVAL; ++ break; ++ case -DWC_E_TIMEOUT: ++ req->status = -ETIMEDOUT; ++ break; ++ default: ++ req->status = status; ++ ++ } ++ req->actual = actual; ++ req->complete(ep_handle, req); ++ } ++ ++ return 0; ++} ++ ++static int _connect(dwc_otg_pcd_t * pcd, int speed) ++{ ++ gadget_wrapper->gadget.speed = speed; ++ return 0; ++} ++ ++static int _disconnect(dwc_otg_pcd_t * pcd) ++{ ++ if (gadget_wrapper->driver && gadget_wrapper->driver->disconnect) { ++ gadget_wrapper->driver->disconnect(&gadget_wrapper->gadget); ++ } ++ return 0; ++} ++ ++static int _resume(dwc_otg_pcd_t * pcd) ++{ ++ if (gadget_wrapper->driver && gadget_wrapper->driver->resume) { ++ gadget_wrapper->driver->resume(&gadget_wrapper->gadget); ++ } ++ ++ return 0; ++} ++ ++static int _suspend(dwc_otg_pcd_t * pcd) ++{ ++ if (gadget_wrapper->driver && gadget_wrapper->driver->suspend) { ++ gadget_wrapper->driver->suspend(&gadget_wrapper->gadget); ++ } ++ return 0; ++} ++ ++/** ++ * This function updates the otg values in the gadget structure. ++ */ ++static int _hnp_changed(dwc_otg_pcd_t * pcd) ++{ ++ ++ if (!gadget_wrapper->gadget.is_otg) ++ return 0; ++ ++ gadget_wrapper->gadget.b_hnp_enable = get_b_hnp_enable(pcd); ++ gadget_wrapper->gadget.a_hnp_support = get_a_hnp_support(pcd); ++ gadget_wrapper->gadget.a_alt_hnp_support = get_a_alt_hnp_support(pcd); ++ return 0; ++} ++ ++static int _reset(dwc_otg_pcd_t * pcd) ++{ ++ return 0; ++} ++ ++#ifdef DWC_UTE_CFI ++static int _cfi_setup(dwc_otg_pcd_t * pcd, void *cfi_req) ++{ ++ int retval = -DWC_E_INVALID; ++ if (gadget_wrapper->driver->cfi_feature_setup) { ++ retval = ++ gadget_wrapper->driver->cfi_feature_setup(&gadget_wrapper-> ++ gadget, ++ (struct ++ cfi_usb_ctrlrequest ++ *)cfi_req); ++ } ++ ++ return retval; ++} ++#endif ++ ++static const struct dwc_otg_pcd_function_ops fops = { ++ .complete = _complete, ++#ifdef DWC_EN_ISOC ++ .isoc_complete = _isoc_complete, ++#endif ++ .setup = _setup, ++ .disconnect = _disconnect, ++ .connect = _connect, ++ .resume = _resume, ++ .suspend = _suspend, ++ .hnp_changed = _hnp_changed, ++ .reset = _reset, ++#ifdef DWC_UTE_CFI ++ .cfi_setup = _cfi_setup, ++#endif ++}; ++ ++/** ++ * This function is the top level PCD interrupt handler. ++ */ ++static irqreturn_t dwc_otg_pcd_irq(int irq, void *dev) ++{ ++ dwc_otg_pcd_t *pcd = dev; ++ int32_t retval = IRQ_NONE; ++ ++ retval = dwc_otg_pcd_handle_intr(pcd); ++ if (retval != 0) { ++ S3C2410X_CLEAR_EINTPEND(); ++ } ++ return IRQ_RETVAL(retval); ++} ++ ++/** ++ * This function initialized the usb_ep structures to there default ++ * state. ++ * ++ * @param d Pointer on gadget_wrapper. ++ */ ++void gadget_add_eps(struct gadget_wrapper *d) ++{ ++ static const char *names[] = { ++ ++ "ep0", ++ "ep1in", ++ "ep2in", ++ "ep3in", ++ "ep4in", ++ "ep5in", ++ "ep6in", ++ "ep7in", ++ "ep8in", ++ "ep9in", ++ "ep10in", ++ "ep11in", ++ "ep12in", ++ "ep13in", ++ "ep14in", ++ "ep15in", ++ "ep1out", ++ "ep2out", ++ "ep3out", ++ "ep4out", ++ "ep5out", ++ "ep6out", ++ "ep7out", ++ "ep8out", ++ "ep9out", ++ "ep10out", ++ "ep11out", ++ "ep12out", ++ "ep13out", ++ "ep14out", ++ "ep15out" ++ }; ++ ++ int i; ++ struct usb_ep *ep; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s\n", __func__); ++ ++ INIT_LIST_HEAD(&d->gadget.ep_list); ++ d->gadget.ep0 = &d->ep0; ++ d->gadget.speed = USB_SPEED_UNKNOWN; ++ ++ INIT_LIST_HEAD(&d->gadget.ep0->ep_list); ++ ++ /** ++ * Initialize the EP0 structure. ++ */ ++ ep = &d->ep0; ++ ++ /* Init the usb_ep structure. */ ++ ep->name = names[0]; ++ ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops; ++ ++ /** ++ * @todo NGS: What should the max packet size be set to ++ * here? Before EP type is set? ++ */ ++ ep->maxpacket = MAX_PACKET_SIZE; ++ dwc_otg_pcd_ep_enable(d->pcd, NULL, ep); ++ ++ list_add_tail(&ep->ep_list, &d->gadget.ep_list); ++ ++ /** ++ * Initialize the EP structures. ++ */ ++ ++ for (i = 0; i < 15; i++) { ++ ep = &d->in_ep[i]; ++ ++ /* Init the usb_ep structure. */ ++ ep->name = names[i + 1]; ++ ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops; ++ ++ /** ++ * @todo NGS: What should the max packet size be set to ++ * here? Before EP type is set? ++ */ ++ ep->maxpacket = MAX_PACKET_SIZE; ++ list_add_tail(&ep->ep_list, &d->gadget.ep_list); ++ } ++ ++ for (i = 0; i < 15; i++) { ++ ep = &d->out_ep[i]; ++ ++ /* Init the usb_ep structure. */ ++ ep->name = names[15 + i + 1]; ++ ep->ops = (struct usb_ep_ops *)&dwc_otg_pcd_ep_ops; ++ ++ /** ++ * @todo NGS: What should the max packet size be set to ++ * here? Before EP type is set? ++ */ ++ ep->maxpacket = MAX_PACKET_SIZE; ++ ++ list_add_tail(&ep->ep_list, &d->gadget.ep_list); ++ } ++ ++ /* remove ep0 from the list. There is a ep0 pointer. */ ++ list_del_init(&d->ep0.ep_list); ++ ++ d->ep0.maxpacket = MAX_EP0_SIZE; ++} ++ ++/** ++ * This function releases the Gadget device. ++ * required by device_unregister(). ++ * ++ * @todo Should this do something? Should it free the PCD? ++ */ ++static void dwc_otg_pcd_gadget_release(struct device *dev) ++{ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p)\n", __func__, dev); ++} ++ ++static struct gadget_wrapper *alloc_wrapper( ++#ifdef LM_INTERFACE ++ struct lm_device *_dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *_dev ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *_dev ++#endif ++ ) ++{ ++#ifdef LM_INTERFACE ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); ++#endif ++ static char pcd_name[] = "dwc_otg_pcd"; ++ ++ struct gadget_wrapper *d; ++ int retval; ++ ++ d = dwc_alloc(sizeof(*d)); ++ if (d == NULL) { ++ return NULL; ++ } ++ ++ memset(d, 0, sizeof(*d)); ++ ++ d->gadget.name = pcd_name; ++ d->pcd = otg_dev->pcd; ++#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) ++ strcpy(d->gadget.dev.bus_id, "gadget"); ++#else ++ /*d->gadget.dev.bus = NULL;*/ ++ d->gadget.dev.init_name = "gadget"; ++#endif ++ d->gadget.dev.parent = &_dev->dev; ++ d->gadget.dev.release = dwc_otg_pcd_gadget_release; ++ d->gadget.ops = &dwc_otg_pcd_ops; ++ d->gadget.max_speed = dwc_otg_pcd_is_dualspeed(otg_dev->pcd)?USB_SPEED_HIGH:0; ++ d->gadget.is_otg = dwc_otg_pcd_is_otg(otg_dev->pcd); ++ ++ d->driver = 0; ++ /* Register the gadget device */ ++ retval = device_register(&d->gadget.dev); ++ if (retval != 0) { ++ DWC_ERROR("device_register failed\n"); ++ dwc_free(d); ++ return NULL; ++ } ++ ++ return d; ++} ++ ++static void free_wrapper(struct gadget_wrapper *d) ++{ ++ if (d->driver) { ++ /* should have been done already by driver model core */ ++ DWC_WARN("driver '%s' is still registered\n", ++ d->driver->driver.name); ++ usb_gadget_unregister_driver(d->driver); ++ } ++ ++ device_unregister(&d->gadget.dev); ++ dwc_free(d); ++} ++ ++/** ++ * This function initialized the PCD portion of the driver. ++ * ++ */ ++int pcd_init( ++#ifdef LM_INTERFACE ++ struct lm_device *_dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *_dev ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *_dev ++#endif ++ ) ++ ++{ ++#ifdef LM_INTERFACE ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); ++#endif ++ int devirq; ++ ++ int retval = 0; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p) otg_dev=%p\n", __func__, _dev, otg_dev); ++ ++ otg_dev->pcd = dwc_otg_pcd_init(otg_dev->core_if); ++ ++ if (!otg_dev->pcd) { ++ DWC_ERROR("dwc_otg_pcd_init failed\n"); ++ return -ENOMEM; ++ } ++ ++ gadget_wrapper = alloc_wrapper(_dev); ++ ++ /* ++ * Initialize EP structures ++ */ ++ gadget_add_eps(gadget_wrapper); ++ ++ /* ++ * Setup interupt handler ++ */ ++#ifdef PLATFORM_INTERFACE ++ devirq = platform_get_irq(_dev, 0); ++#else ++ devirq = _dev->irq; ++#endif ++ DWC_DEBUGPL(DBG_ANY, "registering handler for irq%d\n", devirq); ++ retval = request_irq(devirq, dwc_otg_pcd_irq, ++ IRQF_SHARED, gadget_wrapper->gadget.name, ++ otg_dev->pcd); ++ if (retval != 0) { ++ DWC_ERROR("request of irq%d failed\n", devirq); ++ free_wrapper(gadget_wrapper); ++ return -EBUSY; ++ } ++ ++ dwc_otg_pcd_start(gadget_wrapper->pcd, &fops); ++ ++ return retval; ++} ++ ++/** ++ * Cleanup the PCD. ++ */ ++void pcd_remove( ++#ifdef LM_INTERFACE ++ struct lm_device *_dev ++#elif defined(PCI_INTERFACE) ++ struct pci_dev *_dev ++#elif defined(PLATFORM_INTERFACE) ++ struct platform_device *_dev ++#endif ++ ) ++{ ++#ifdef LM_INTERFACE ++ dwc_otg_device_t *otg_dev = lm_get_drvdata(_dev); ++#elif defined(PCI_INTERFACE) ++ dwc_otg_device_t *otg_dev = pci_get_drvdata(_dev); ++#elif defined(PLATFORM_INTERFACE) ++ dwc_otg_device_t *otg_dev = platform_get_drvdata(_dev); ++#endif ++ dwc_otg_pcd_t *pcd = otg_dev->pcd; ++ ++ DWC_DEBUGPL(DBG_PCDV, "%s(%p) otg_dev %p\n", __func__, _dev, otg_dev); ++ ++ /* ++ * Free the IRQ ++ */ ++#ifdef PLATFORM_INTERFACE ++ free_irq(platform_get_irq(_dev, 0), pcd); ++#else ++ free_irq(_dev->irq, pcd); ++#endif ++ dwc_otg_pcd_remove(otg_dev->pcd); ++ free_wrapper(gadget_wrapper); ++ otg_dev->pcd = 0; ++} ++ ++/** ++ * This function registers a gadget driver with the PCD. ++ * ++ * When a driver is successfully registered, it will receive control ++ * requests including set_configuration(), which enables non-control ++ * requests. then usb traffic follows until a disconnect is reported. ++ * then a host may connect again, or the driver might get unbound. ++ * ++ * @param driver The driver being registered ++ */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++int usb_gadget_register_driver(struct usb_gadget_driver *driver) ++#else ++int usb_gadget_probe_driver(struct usb_gadget_driver *driver, ++ int (*bind)(struct usb_gadget *)) ++#endif ++{ ++ int retval; ++ ++ DWC_DEBUGPL(DBG_PCD, "registering gadget driver '%s'\n", ++ driver->driver.name); ++ ++ if (!driver || driver->max_speed == USB_SPEED_UNKNOWN || ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++ !driver->bind || ++#else ++ !bind || ++#endif ++ !driver->unbind || !driver->disconnect || !driver->setup) { ++ DWC_DEBUGPL(DBG_PCDV, "EINVAL\n"); ++ return -EINVAL; ++ } ++ if (gadget_wrapper == 0) { ++ DWC_DEBUGPL(DBG_PCDV, "ENODEV\n"); ++ return -ENODEV; ++ } ++ if (gadget_wrapper->driver != 0) { ++ DWC_DEBUGPL(DBG_PCDV, "EBUSY (%p)\n", gadget_wrapper->driver); ++ return -EBUSY; ++ } ++ ++ /* hook up the driver */ ++ gadget_wrapper->driver = driver; ++ gadget_wrapper->gadget.dev.driver = &driver->driver; ++ ++ DWC_DEBUGPL(DBG_PCD, "bind to driver %s\n", driver->driver.name); ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++ retval = driver->bind(&gadget_wrapper->gadget); ++#else ++ retval = bind(&gadget_wrapper->gadget); ++#endif ++ if (retval) { ++ DWC_ERROR("bind to driver %s --> error %d\n", ++ driver->driver.name, retval); ++ gadget_wrapper->driver = 0; ++ gadget_wrapper->gadget.dev.driver = 0; ++ return retval; ++ } ++ DWC_DEBUGPL(DBG_ANY, "registered gadget driver '%s'\n", ++ driver->driver.name); ++ return 0; ++} ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,37) ++EXPORT_SYMBOL(usb_gadget_register_driver); ++#else ++EXPORT_SYMBOL(usb_gadget_probe_driver); ++#endif ++ ++/** ++ * This function unregisters a gadget driver ++ * ++ * @param driver The driver being unregistered ++ */ ++int usb_gadget_unregister_driver(struct usb_gadget_driver *driver) ++{ ++ //DWC_DEBUGPL(DBG_PCDV,"%s(%p)\n", __func__, _driver); ++ ++ if (gadget_wrapper == 0) { ++ DWC_DEBUGPL(DBG_ANY, "%s Return(%d): s_pcd==0\n", __func__, ++ -ENODEV); ++ return -ENODEV; ++ } ++ if (driver == 0 || driver != gadget_wrapper->driver) { ++ DWC_DEBUGPL(DBG_ANY, "%s Return(%d): driver?\n", __func__, ++ -EINVAL); ++ return -EINVAL; ++ } ++ ++ driver->unbind(&gadget_wrapper->gadget); ++ gadget_wrapper->driver = 0; ++ ++ DWC_DEBUGPL(DBG_ANY, "unregistered driver '%s'\n", driver->driver.name); ++ return 0; ++} ++ ++EXPORT_SYMBOL(usb_gadget_unregister_driver); ++ ++#endif /* DWC_HOST_ONLY */ +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/dwc_otg_regs.h +@@ -0,0 +1,2237 @@ ++/* ========================================================================== ++ * $File: //dwh/usb_iip/dev/software/otg/linux/drivers/dwc_otg_regs.h $ ++ * $Revision: #76 $ ++ * $Date: 2009/04/02 $ ++ * $Change: 1224216 $ ++ * ++ * Synopsys HS OTG Linux Software Driver and documentation (hereinafter, ++ * "Software") is an Unsupported proprietary work of Synopsys, Inc. unless ++ * otherwise expressly agreed to in writing between Synopsys and you. ++ * ++ * The Software IS NOT an item of Licensed Software or Licensed Product under ++ * any End User Software License Agreement or Agreement for Licensed Product ++ * with Synopsys or any supplement thereto. You are permitted to use and ++ * redistribute this Software in source and binary forms, with or without ++ * modification, provided that redistributions of source code must retain this ++ * notice. You may not view, use, disclose, copy or distribute this file or ++ * any information contained herein except pursuant to this license grant from ++ * Synopsys. If you do not agree with this notice, including the disclaimer ++ * below, then you are not authorized to use the Software. ++ * ++ * THIS SOFTWARE IS BEING DISTRIBUTED BY SYNOPSYS SOLELY ON AN "AS IS" BASIS ++ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE ++ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ++ * ARE HEREBY DISCLAIMED. IN NO EVENT SHALL SYNOPSYS BE LIABLE FOR ANY DIRECT, ++ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES ++ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR ++ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER ++ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT ++ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY ++ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ++ * DAMAGE. ++ * ========================================================================== */ ++ ++#ifndef __DWC_OTG_REGS_H__ ++#define __DWC_OTG_REGS_H__ ++ ++#include "dwc_otg_core_if.h" ++ ++/** ++ * @file ++ * ++ * This file contains the data structures for accessing the DWC_otg core registers. ++ * ++ * The application interfaces with the HS OTG core by reading from and ++ * writing to the Control and Status Register (CSR) space through the ++ * AHB Slave interface. These registers are 32 bits wide, and the ++ * addresses are 32-bit-block aligned. ++ * CSRs are classified as follows: ++ * - Core Global Registers ++ * - Device Mode Registers ++ * - Device Global Registers ++ * - Device Endpoint Specific Registers ++ * - Host Mode Registers ++ * - Host Global Registers ++ * - Host Port CSRs ++ * - Host Channel Specific Registers ++ * ++ * Only the Core Global registers can be accessed in both Device and ++ * Host modes. When the HS OTG core is operating in one mode, either ++ * Device or Host, the application must not access registers from the ++ * other mode. When the core switches from one mode to another, the ++ * registers in the new mode of operation must be reprogrammed as they ++ * would be after a power-on reset. ++ */ ++ ++/****************************************************************************/ ++/** DWC_otg Core registers . ++ * The dwc_otg_core_global_regs structure defines the size ++ * and relative field offsets for the Core Global registers. ++ */ ++typedef struct dwc_otg_core_global_regs { ++ /** OTG Control and Status Register. <i>Offset: 000h</i> */ ++ volatile uint32_t gotgctl; ++ /** OTG Interrupt Register. <i>Offset: 004h</i> */ ++ volatile uint32_t gotgint; ++ /**Core AHB Configuration Register. <i>Offset: 008h</i> */ ++ volatile uint32_t gahbcfg; ++ ++#define DWC_GLBINTRMASK 0x0001 ++#define DWC_DMAENABLE 0x0020 ++#define DWC_NPTXEMPTYLVL_EMPTY 0x0080 ++#define DWC_NPTXEMPTYLVL_HALFEMPTY 0x0000 ++#define DWC_PTXEMPTYLVL_EMPTY 0x0100 ++#define DWC_PTXEMPTYLVL_HALFEMPTY 0x0000 ++ ++ /**Core USB Configuration Register. <i>Offset: 00Ch</i> */ ++ volatile uint32_t gusbcfg; ++ /**Core Reset Register. <i>Offset: 010h</i> */ ++ volatile uint32_t grstctl; ++ /**Core Interrupt Register. <i>Offset: 014h</i> */ ++ volatile uint32_t gintsts; ++ /**Core Interrupt Mask Register. <i>Offset: 018h</i> */ ++ volatile uint32_t gintmsk; ++ /**Receive Status Queue Read Register (Read Only). <i>Offset: 01Ch</i> */ ++ volatile uint32_t grxstsr; ++ /**Receive Status Queue Read & POP Register (Read Only). <i>Offset: 020h</i>*/ ++ volatile uint32_t grxstsp; ++ /**Receive FIFO Size Register. <i>Offset: 024h</i> */ ++ volatile uint32_t grxfsiz; ++ /**Non Periodic Transmit FIFO Size Register. <i>Offset: 028h</i> */ ++ volatile uint32_t gnptxfsiz; ++ /**Non Periodic Transmit FIFO/Queue Status Register (Read ++ * Only). <i>Offset: 02Ch</i> */ ++ volatile uint32_t gnptxsts; ++ /**I2C Access Register. <i>Offset: 030h</i> */ ++ volatile uint32_t gi2cctl; ++ /**PHY Vendor Control Register. <i>Offset: 034h</i> */ ++ volatile uint32_t gpvndctl; ++ /**General Purpose Input/Output Register. <i>Offset: 038h</i> */ ++ volatile uint32_t ggpio; ++ /**User ID Register. <i>Offset: 03Ch</i> */ ++ volatile uint32_t guid; ++ /**Synopsys ID Register (Read Only). <i>Offset: 040h</i> */ ++ volatile uint32_t gsnpsid; ++ /**User HW Config1 Register (Read Only). <i>Offset: 044h</i> */ ++ volatile uint32_t ghwcfg1; ++ /**User HW Config2 Register (Read Only). <i>Offset: 048h</i> */ ++ volatile uint32_t ghwcfg2; ++#define DWC_SLAVE_ONLY_ARCH 0 ++#define DWC_EXT_DMA_ARCH 1 ++#define DWC_INT_DMA_ARCH 2 ++ ++#define DWC_MODE_HNP_SRP_CAPABLE 0 ++#define DWC_MODE_SRP_ONLY_CAPABLE 1 ++#define DWC_MODE_NO_HNP_SRP_CAPABLE 2 ++#define DWC_MODE_SRP_CAPABLE_DEVICE 3 ++#define DWC_MODE_NO_SRP_CAPABLE_DEVICE 4 ++#define DWC_MODE_SRP_CAPABLE_HOST 5 ++#define DWC_MODE_NO_SRP_CAPABLE_HOST 6 ++ ++ /**User HW Config3 Register (Read Only). <i>Offset: 04Ch</i> */ ++ volatile uint32_t ghwcfg3; ++ /**User HW Config4 Register (Read Only). <i>Offset: 050h</i>*/ ++ volatile uint32_t ghwcfg4; ++ /** Core LPM Configuration register */ ++ volatile uint32_t glpmcfg; ++ /** Reserved <i>Offset: 058h-0FFh</i> */ ++ volatile uint32_t reserved[42]; ++ /** Host Periodic Transmit FIFO Size Register. <i>Offset: 100h</i> */ ++ volatile uint32_t hptxfsiz; ++ /** Device Periodic Transmit FIFO#n Register if dedicated fifos are disabled, ++ otherwise Device Transmit FIFO#n Register. ++ * <i>Offset: 104h + (FIFO_Number-1)*04h, 1 <= FIFO Number <= 15 (1<=n<=15).</i> */ ++ volatile uint32_t dptxfsiz_dieptxf[15]; ++} dwc_otg_core_global_regs_t; ++ ++/** ++ * This union represents the bit fields of the Core OTG Control ++ * and Status Register (GOTGCTL). Set the bits using the bit ++ * fields then write the <i>d32</i> value to the register. ++ */ ++typedef union gotgctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned sesreqscs:1; ++ unsigned sesreq:1; ++ unsigned reserved2_7:6; ++ unsigned hstnegscs:1; ++ unsigned hnpreq:1; ++ unsigned hstsethnpen:1; ++ unsigned devhnpen:1; ++ unsigned reserved12_15:4; ++ unsigned conidsts:1; ++ unsigned reserved17:1; ++ unsigned asesvld:1; ++ unsigned bsesvld:1; ++ unsigned currmod:1; ++ unsigned reserved21_31:11; ++ } b; ++} gotgctl_data_t; ++ ++/** ++ * This union represents the bit fields of the Core OTG Interrupt Register ++ * (GOTGINT). Set/clear the bits using the bit fields then write the <i>d32</i> ++ * value to the register. ++ */ ++typedef union gotgint_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Current Mode */ ++ unsigned reserved0_1:2; ++ ++ /** Session End Detected */ ++ unsigned sesenddet:1; ++ ++ unsigned reserved3_7:5; ++ ++ /** Session Request Success Status Change */ ++ unsigned sesreqsucstschng:1; ++ /** Host Negotiation Success Status Change */ ++ unsigned hstnegsucstschng:1; ++ ++ unsigned reserver10_16:7; ++ ++ /** Host Negotiation Detected */ ++ unsigned hstnegdet:1; ++ /** A-Device Timeout Change */ ++ unsigned adevtoutchng:1; ++ /** Debounce Done */ ++ unsigned debdone:1; ++ ++ unsigned reserved31_20:12; ++ ++ } b; ++} gotgint_data_t; ++ ++/** ++ * This union represents the bit fields of the Core AHB Configuration ++ * Register (GAHBCFG). Set/clear the bits using the bit fields then ++ * write the <i>d32</i> value to the register. ++ */ ++typedef union gahbcfg_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned glblintrmsk:1; ++#define DWC_GAHBCFG_GLBINT_ENABLE 1 ++ ++ unsigned hburstlen:4; ++#define DWC_GAHBCFG_INT_DMA_BURST_SINGLE 0 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR 1 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR4 3 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR8 5 ++#define DWC_GAHBCFG_INT_DMA_BURST_INCR16 7 ++ ++ unsigned dmaenable:1; ++#define DWC_GAHBCFG_DMAENABLE 1 ++ unsigned reserved:1; ++ unsigned nptxfemplvl_txfemplvl:1; ++ unsigned ptxfemplvl:1; ++#define DWC_GAHBCFG_TXFEMPTYLVL_EMPTY 1 ++#define DWC_GAHBCFG_TXFEMPTYLVL_HALFEMPTY 0 ++ unsigned reserved9_31:23; ++ } b; ++} gahbcfg_data_t; ++ ++/** ++ * This union represents the bit fields of the Core USB Configuration ++ * Register (GUSBCFG). Set the bits using the bit fields then write ++ * the <i>d32</i> value to the register. ++ */ ++typedef union gusbcfg_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned toutcal:3; ++ unsigned phyif:1; ++ unsigned ulpi_utmi_sel:1; ++ unsigned fsintf:1; ++ unsigned physel:1; ++ unsigned ddrsel:1; ++ unsigned srpcap:1; ++ unsigned hnpcap:1; ++ unsigned usbtrdtim:4; ++ unsigned nptxfrwnden:1; ++ unsigned phylpwrclksel:1; ++ unsigned otgutmifssel:1; ++ unsigned ulpi_fsls:1; ++ unsigned ulpi_auto_res:1; ++ unsigned ulpi_clk_sus_m:1; ++ unsigned ulpi_ext_vbus_drv:1; ++ unsigned ulpi_int_vbus_indicator:1; ++ unsigned term_sel_dl_pulse:1; ++ unsigned reserved23_25:3; ++ unsigned ic_usb_cap:1; ++ unsigned ic_traffic_pull_remove:1; ++ unsigned tx_end_delay:1; ++ unsigned reserved29_31:3; ++ } b; ++} gusbcfg_data_t; ++ ++/** ++ * This union represents the bit fields of the Core LPM Configuration ++ * Register (GLPMCFG). Set the bits using bit fields then write ++ * the <i>d32</i> value to the register. ++ */ ++typedef union glpmctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** LPM-Capable (LPMCap) (Device and Host) ++ * The application uses this bit to control ++ * the DWC_otg core LPM capabilities. ++ */ ++ unsigned lpm_cap_en:1; ++ /** LPM response programmed by application (AppL1Res) (Device) ++ * Handshake response to LPM token pre-programmed ++ * by device application software. ++ */ ++ unsigned appl_resp:1; ++ /** Host Initiated Resume Duration (HIRD) (Device and Host) ++ * In Host mode this field indicates the value of HIRD ++ * to be sent in an LPM transaction. ++ * In Device mode this field is updated with the ++ * Received LPM Token HIRD bmAttribute ++ * when an ACK/NYET/STALL response is sent ++ * to an LPM transaction. ++ */ ++ unsigned hird:4; ++ /** RemoteWakeEnable (bRemoteWake) (Device and Host) ++ * In Host mode this bit indicates the value of remote ++ * wake up to be sent in wIndex field of LPM transaction. ++ * In Device mode this field is updated with the ++ * Received LPM Token bRemoteWake bmAttribute ++ * when an ACK/NYET/STALL response is sent ++ * to an LPM transaction. ++ */ ++ unsigned rem_wkup_en:1; ++ /** Enable utmi_sleep_n (EnblSlpM) (Device and Host) ++ * The application uses this bit to control ++ * the utmi_sleep_n assertion to the PHY when in L1 state. ++ */ ++ unsigned en_utmi_sleep:1; ++ /** HIRD Threshold (HIRD_Thres) (Device and Host) ++ */ ++ unsigned hird_thres:5; ++ /** LPM Response (CoreL1Res) (Device and Host) ++ * In Host mode this bit contains handsake response to ++ * LPM transaction. ++ * In Device mode the response of the core to ++ * LPM transaction received is reflected in these two bits. ++ - 0x0 : ERROR (No handshake response) ++ - 0x1 : STALL ++ - 0x2 : NYET ++ - 0x3 : ACK ++ */ ++ unsigned lpm_resp:2; ++ /** Port Sleep Status (SlpSts) (Device and Host) ++ * This bit is set as long as a Sleep condition ++ * is present on the USB bus. ++ */ ++ unsigned prt_sleep_sts:1; ++ /** Sleep State Resume OK (L1ResumeOK) (Device and Host) ++ * Indicates that the application or host ++ * can start resume from Sleep state. ++ */ ++ unsigned sleep_state_resumeok:1; ++ /** LPM channel Index (LPM_Chnl_Indx) (Host) ++ * The channel number on which the LPM transaction ++ * has to be applied while sending ++ * an LPM transaction to the local device. ++ */ ++ unsigned lpm_chan_index:4; ++ /** LPM Retry Count (LPM_Retry_Cnt) (Host) ++ * Number host retries that would be performed ++ * if the device response was not valid response. ++ */ ++ unsigned retry_count:3; ++ /** Send LPM Transaction (SndLPM) (Host) ++ * When set by application software, ++ * an LPM transaction containing two tokens ++ * is sent. ++ */ ++ unsigned send_lpm:1; ++ /** LPM Retry status (LPM_RetryCnt_Sts) (Host) ++ * Number of LPM Host Retries still remaining ++ * to be transmitted for the current LPM sequence ++ */ ++ unsigned retry_count_sts:3; ++ unsigned reserved28_29:2; ++ /** In host mode once this bit is set, the host ++ * configures to drive the HSIC Idle state on the bus. ++ * It then waits for the device to initiate the Connect sequence. ++ * In device mode once this bit is set, the device waits for ++ * the HSIC Idle line state on the bus. Upon receving the Idle ++ * line state, it initiates the HSIC Connect sequence. ++ */ ++ unsigned hsic_connect:1; ++ /** This bit overrides and functionally inverts ++ * the if_select_hsic input port signal. ++ */ ++ unsigned inv_sel_hsic:1; ++ } b; ++} glpmcfg_data_t; ++ ++/** ++ * This union represents the bit fields of the Core Reset Register ++ * (GRSTCTL). Set/clear the bits using the bit fields then write the ++ * <i>d32</i> value to the register. ++ */ ++typedef union grstctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Core Soft Reset (CSftRst) (Device and Host) ++ * ++ * The application can flush the control logic in the ++ * entire core using this bit. This bit resets the ++ * pipelines in the AHB Clock domain as well as the ++ * PHY Clock domain. ++ * ++ * The state machines are reset to an IDLE state, the ++ * control bits in the CSRs are cleared, all the ++ * transmit FIFOs and the receive FIFO are flushed. ++ * ++ * The status mask bits that control the generation of ++ * the interrupt, are cleared, to clear the ++ * interrupt. The interrupt status bits are not ++ * cleared, so the application can get the status of ++ * any events that occurred in the core after it has ++ * set this bit. ++ * ++ * Any transactions on the AHB are terminated as soon ++ * as possible following the protocol. Any ++ * transactions on the USB are terminated immediately. ++ * ++ * The configuration settings in the CSRs are ++ * unchanged, so the software doesn't have to ++ * reprogram these registers (Device ++ * Configuration/Host Configuration/Core System ++ * Configuration/Core PHY Configuration). ++ * ++ * The application can write to this bit, any time it ++ * wants to reset the core. This is a self clearing ++ * bit and the core clears this bit after all the ++ * necessary logic is reset in the core, which may ++ * take several clocks, depending on the current state ++ * of the core. ++ */ ++ unsigned csftrst:1; ++ /** Hclk Soft Reset ++ * ++ * The application uses this bit to reset the control logic in ++ * the AHB clock domain. Only AHB clock domain pipelines are ++ * reset. ++ */ ++ unsigned hsftrst:1; ++ /** Host Frame Counter Reset (Host Only)<br> ++ * ++ * The application can reset the (micro)frame number ++ * counter inside the core, using this bit. When the ++ * (micro)frame counter is reset, the subsequent SOF ++ * sent out by the core, will have a (micro)frame ++ * number of 0. ++ */ ++ unsigned hstfrm:1; ++ /** In Token Sequence Learning Queue Flush ++ * (INTknQFlsh) (Device Only) ++ */ ++ unsigned intknqflsh:1; ++ /** RxFIFO Flush (RxFFlsh) (Device and Host) ++ * ++ * The application can flush the entire Receive FIFO ++ * using this bit. <p>The application must first ++ * ensure that the core is not in the middle of a ++ * transaction. <p>The application should write into ++ * this bit, only after making sure that neither the ++ * DMA engine is reading from the RxFIFO nor the MAC ++ * is writing the data in to the FIFO. <p>The ++ * application should wait until the bit is cleared ++ * before performing any other operations. This bit ++ * will takes 8 clocks (slowest of PHY or AHB clock) ++ * to clear. ++ */ ++ unsigned rxfflsh:1; ++ /** TxFIFO Flush (TxFFlsh) (Device and Host). ++ * ++ * This bit is used to selectively flush a single or ++ * all transmit FIFOs. The application must first ++ * ensure that the core is not in the middle of a ++ * transaction. <p>The application should write into ++ * this bit, only after making sure that neither the ++ * DMA engine is writing into the TxFIFO nor the MAC ++ * is reading the data out of the FIFO. <p>The ++ * application should wait until the core clears this ++ * bit, before performing any operations. This bit ++ * will takes 8 clocks (slowest of PHY or AHB clock) ++ * to clear. ++ */ ++ unsigned txfflsh:1; ++ ++ /** TxFIFO Number (TxFNum) (Device and Host). ++ * ++ * This is the FIFO number which needs to be flushed, ++ * using the TxFIFO Flush bit. This field should not ++ * be changed until the TxFIFO Flush bit is cleared by ++ * the core. ++ * - 0x0 : Non Periodic TxFIFO Flush ++ * - 0x1 : Periodic TxFIFO #1 Flush in device mode ++ * or Periodic TxFIFO in host mode ++ * - 0x2 : Periodic TxFIFO #2 Flush in device mode. ++ * - ... ++ * - 0xF : Periodic TxFIFO #15 Flush in device mode ++ * - 0x10: Flush all the Transmit NonPeriodic and ++ * Transmit Periodic FIFOs in the core ++ */ ++ unsigned txfnum:5; ++ /** Reserved */ ++ unsigned reserved11_29:19; ++ /** DMA Request Signal. Indicated DMA request is in ++ * probress. Used for debug purpose. */ ++ unsigned dmareq:1; ++ /** AHB Master Idle. Indicates the AHB Master State ++ * Machine is in IDLE condition. */ ++ unsigned ahbidle:1; ++ } b; ++} grstctl_t; ++ ++/** ++ * This union represents the bit fields of the Core Interrupt Mask ++ * Register (GINTMSK). Set/clear the bits using the bit fields then ++ * write the <i>d32</i> value to the register. ++ */ ++typedef union gintmsk_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned reserved0:1; ++ unsigned modemismatch:1; ++ unsigned otgintr:1; ++ unsigned sofintr:1; ++ unsigned rxstsqlvl:1; ++ unsigned nptxfempty:1; ++ unsigned ginnakeff:1; ++ unsigned goutnakeff:1; ++ unsigned reserved8:1; ++ unsigned i2cintr:1; ++ unsigned erlysuspend:1; ++ unsigned usbsuspend:1; ++ unsigned usbreset:1; ++ unsigned enumdone:1; ++ unsigned isooutdrop:1; ++ unsigned eopframe:1; ++ unsigned reserved16:1; ++ unsigned epmismatch:1; ++ unsigned inepintr:1; ++ unsigned outepintr:1; ++ unsigned incomplisoin:1; ++ unsigned incomplisoout:1; ++ unsigned reserved22_23:2; ++ unsigned portintr:1; ++ unsigned hcintr:1; ++ unsigned ptxfempty:1; ++ unsigned lpmtranrcvd:1; ++ unsigned conidstschng:1; ++ unsigned disconnect:1; ++ unsigned sessreqintr:1; ++ unsigned wkupintr:1; ++ } b; ++} gintmsk_data_t; ++/** ++ * This union represents the bit fields of the Core Interrupt Register ++ * (GINTSTS). Set/clear the bits using the bit fields then write the ++ * <i>d32</i> value to the register. ++ */ ++typedef union gintsts_data { ++ /** raw register data */ ++ uint32_t d32; ++#define DWC_SOF_INTR_MASK 0x0008 ++ /** register bits */ ++ struct { ++#define DWC_HOST_MODE 1 ++ unsigned curmode:1; ++ unsigned modemismatch:1; ++ unsigned otgintr:1; ++ unsigned sofintr:1; ++ unsigned rxstsqlvl:1; ++ unsigned nptxfempty:1; ++ unsigned ginnakeff:1; ++ unsigned goutnakeff:1; ++ unsigned reserved8:1; ++ unsigned i2cintr:1; ++ unsigned erlysuspend:1; ++ unsigned usbsuspend:1; ++ unsigned usbreset:1; ++ unsigned enumdone:1; ++ unsigned isooutdrop:1; ++ unsigned eopframe:1; ++ unsigned intokenrx:1; ++ unsigned epmismatch:1; ++ unsigned inepint:1; ++ unsigned outepintr:1; ++ unsigned incomplisoin:1; ++ unsigned incomplisoout:1; ++ unsigned reserved22_23:2; ++ unsigned portintr:1; ++ unsigned hcintr:1; ++ unsigned ptxfempty:1; ++ unsigned lpmtranrcvd:1; ++ unsigned conidstschng:1; ++ unsigned disconnect:1; ++ unsigned sessreqintr:1; ++ unsigned wkupintr:1; ++ } b; ++} gintsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Device Receive Status Read and ++ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the <i>d32</i> ++ * element then read out the bits using the <i>b</i>it elements. ++ */ ++typedef union device_grxsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned epnum:4; ++ unsigned bcnt:11; ++ unsigned dpid:2; ++ ++#define DWC_STS_DATA_UPDT 0x2 // OUT Data Packet ++#define DWC_STS_XFER_COMP 0x3 // OUT Data Transfer Complete ++ ++#define DWC_DSTS_GOUT_NAK 0x1 // Global OUT NAK ++#define DWC_DSTS_SETUP_COMP 0x4 // Setup Phase Complete ++#define DWC_DSTS_SETUP_UPDT 0x6 // SETUP Packet ++ unsigned pktsts:4; ++ unsigned fn:4; ++ unsigned reserved:7; ++ } b; ++} device_grxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Receive Status Read and ++ * Pop Registers (GRXSTSR, GRXSTSP) Read the register into the <i>d32</i> ++ * element then read out the bits using the <i>b</i>it elements. ++ */ ++typedef union host_grxsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned chnum:4; ++ unsigned bcnt:11; ++ unsigned dpid:2; ++ ++ unsigned pktsts:4; ++#define DWC_GRXSTS_PKTSTS_IN 0x2 ++#define DWC_GRXSTS_PKTSTS_IN_XFER_COMP 0x3 ++#define DWC_GRXSTS_PKTSTS_DATA_TOGGLE_ERR 0x5 ++#define DWC_GRXSTS_PKTSTS_CH_HALTED 0x7 ++ ++ unsigned reserved:11; ++ } b; ++} host_grxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the FIFO Size Registers (HPTXFSIZ, ++ * GNPTXFSIZ, DPTXFSIZn, DIEPTXFn). Read the register into the <i>d32</i> element then ++ * read out the bits using the <i>b</i>it elements. ++ */ ++typedef union fifosize_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned startaddr:16; ++ unsigned depth:16; ++ } b; ++} fifosize_data_t; ++ ++/** ++ * This union represents the bit fields in the Non-Periodic Transmit ++ * FIFO/Queue Status Register (GNPTXSTS). Read the register into the ++ * <i>d32</i> element then read out the bits using the <i>b</i>it ++ * elements. ++ */ ++typedef union gnptxsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned nptxfspcavail:16; ++ unsigned nptxqspcavail:8; ++ /** Top of the Non-Periodic Transmit Request Queue ++ * - bit 24 - Terminate (Last entry for the selected ++ * channel/EP) ++ * - bits 26:25 - Token Type ++ * - 2'b00 - IN/OUT ++ * - 2'b01 - Zero Length OUT ++ * - 2'b10 - PING/Complete Split ++ * - 2'b11 - Channel Halt ++ * - bits 30:27 - Channel/EP Number ++ */ ++ unsigned nptxqtop_terminate:1; ++ unsigned nptxqtop_token:2; ++ unsigned nptxqtop_chnep:4; ++ unsigned reserved:1; ++ } b; ++} gnptxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Transmit ++ * FIFO Status Register (DTXFSTS). Read the register into the ++ * <i>d32</i> element then read out the bits using the <i>b</i>it ++ * elements. ++ */ ++typedef union dtxfsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned txfspcavail:16; ++ unsigned reserved:16; ++ } b; ++} dtxfsts_data_t; ++ ++/** ++ * This union represents the bit fields in the I2C Control Register ++ * (I2CCTL). Read the register into the <i>d32</i> element then read out the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union gi2cctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned rwdata:8; ++ unsigned regaddr:8; ++ unsigned addr:7; ++ unsigned i2cen:1; ++ unsigned ack:1; ++ unsigned i2csuspctl:1; ++ unsigned i2cdevaddr:2; ++ unsigned reserved:2; ++ unsigned rw:1; ++ unsigned bsydne:1; ++ } b; ++} gi2cctl_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config1 ++ * Register. Read the register into the <i>d32</i> element then read ++ * out the bits using the <i>b</i>it elements. ++ */ ++typedef union hwcfg1_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned ep_dir0:2; ++ unsigned ep_dir1:2; ++ unsigned ep_dir2:2; ++ unsigned ep_dir3:2; ++ unsigned ep_dir4:2; ++ unsigned ep_dir5:2; ++ unsigned ep_dir6:2; ++ unsigned ep_dir7:2; ++ unsigned ep_dir8:2; ++ unsigned ep_dir9:2; ++ unsigned ep_dir10:2; ++ unsigned ep_dir11:2; ++ unsigned ep_dir12:2; ++ unsigned ep_dir13:2; ++ unsigned ep_dir14:2; ++ unsigned ep_dir15:2; ++ } b; ++} hwcfg1_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config2 ++ * Register. Read the register into the <i>d32</i> element then read ++ * out the bits using the <i>b</i>it elements. ++ */ ++typedef union hwcfg2_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /* GHWCFG2 */ ++ unsigned op_mode:3; ++#define DWC_HWCFG2_OP_MODE_HNP_SRP_CAPABLE_OTG 0 ++#define DWC_HWCFG2_OP_MODE_SRP_ONLY_CAPABLE_OTG 1 ++#define DWC_HWCFG2_OP_MODE_NO_HNP_SRP_CAPABLE_OTG 2 ++#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_DEVICE 3 ++#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_DEVICE 4 ++#define DWC_HWCFG2_OP_MODE_SRP_CAPABLE_HOST 5 ++#define DWC_HWCFG2_OP_MODE_NO_SRP_CAPABLE_HOST 6 ++ ++ unsigned architecture:2; ++ unsigned point2point:1; ++ unsigned hs_phy_type:2; ++#define DWC_HWCFG2_HS_PHY_TYPE_NOT_SUPPORTED 0 ++#define DWC_HWCFG2_HS_PHY_TYPE_UTMI 1 ++#define DWC_HWCFG2_HS_PHY_TYPE_ULPI 2 ++#define DWC_HWCFG2_HS_PHY_TYPE_UTMI_ULPI 3 ++ ++ unsigned fs_phy_type:2; ++ unsigned num_dev_ep:4; ++ unsigned num_host_chan:4; ++ unsigned perio_ep_supported:1; ++ unsigned dynamic_fifo:1; ++ unsigned multi_proc_int:1; ++ unsigned reserved21:1; ++ unsigned nonperio_tx_q_depth:2; ++ unsigned host_perio_tx_q_depth:2; ++ unsigned dev_token_q_depth:5; ++ unsigned reserved31:1; ++ } b; ++} hwcfg2_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config3 ++ * Register. Read the register into the <i>d32</i> element then read ++ * out the bits using the <i>b</i>it elements. ++ */ ++typedef union hwcfg3_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /* GHWCFG3 */ ++ unsigned xfer_size_cntr_width:4; ++ unsigned packet_size_cntr_width:3; ++ unsigned otg_func:1; ++ unsigned i2c:1; ++ unsigned vendor_ctrl_if:1; ++ unsigned optional_features:1; ++ unsigned synch_reset_type:1; ++ unsigned otg_enable_ic_usb:1; ++ unsigned otg_enable_hsic:1; ++ unsigned reserved14:1; ++ unsigned otg_lpm_en:1; ++ unsigned dfifo_depth:16; ++ } b; ++} hwcfg3_data_t; ++ ++/** ++ * This union represents the bit fields in the User HW Config4 ++ * Register. Read the register into the <i>d32</i> element then read ++ * out the bits using the <i>b</i>it elements. ++ */ ++typedef union hwcfg4_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned num_dev_perio_in_ep:4; ++ unsigned power_optimiz:1; ++ unsigned min_ahb_freq:9; ++ unsigned utmi_phy_data_width:2; ++ unsigned num_dev_mode_ctrl_ep:4; ++ unsigned iddig_filt_en:1; ++ unsigned vbus_valid_filt_en:1; ++ unsigned a_valid_filt_en:1; ++ unsigned b_valid_filt_en:1; ++ unsigned session_end_filt_en:1; ++ unsigned ded_fifo_en:1; ++ unsigned num_in_eps:4; ++ unsigned desc_dma:1; ++ unsigned desc_dma_dyn:1; ++ } b; ++} hwcfg4_data_t; ++ ++//////////////////////////////////////////// ++// Device Registers ++/** ++ * Device Global Registers. <i>Offsets 800h-BFFh</i> ++ * ++ * The following structures define the size and relative field offsets ++ * for the Device Mode Registers. ++ * ++ * <i>These registers are visible only in Device mode and must not be ++ * accessed in Host mode, as the results are unknown.</i> ++ */ ++typedef struct dwc_otg_dev_global_regs { ++ /** Device Configuration Register. <i>Offset 800h</i> */ ++ volatile uint32_t dcfg; ++ /** Device Control Register. <i>Offset: 804h</i> */ ++ volatile uint32_t dctl; ++ /** Device Status Register (Read Only). <i>Offset: 808h</i> */ ++ volatile uint32_t dsts; ++ /** Reserved. <i>Offset: 80Ch</i> */ ++ uint32_t unused; ++ /** Device IN Endpoint Common Interrupt Mask ++ * Register. <i>Offset: 810h</i> */ ++ volatile uint32_t diepmsk; ++ /** Device OUT Endpoint Common Interrupt Mask ++ * Register. <i>Offset: 814h</i> */ ++ volatile uint32_t doepmsk; ++ /** Device All Endpoints Interrupt Register. <i>Offset: 818h</i> */ ++ volatile uint32_t daint; ++ /** Device All Endpoints Interrupt Mask Register. <i>Offset: ++ * 81Ch</i> */ ++ volatile uint32_t daintmsk; ++ /** Device IN Token Queue Read Register-1 (Read Only). ++ * <i>Offset: 820h</i> */ ++ volatile uint32_t dtknqr1; ++ /** Device IN Token Queue Read Register-2 (Read Only). ++ * <i>Offset: 824h</i> */ ++ volatile uint32_t dtknqr2; ++ /** Device VBUS discharge Register. <i>Offset: 828h</i> */ ++ volatile uint32_t dvbusdis; ++ /** Device VBUS Pulse Register. <i>Offset: 82Ch</i> */ ++ volatile uint32_t dvbuspulse; ++ /** Device IN Token Queue Read Register-3 (Read Only). / ++ * Device Thresholding control register (Read/Write) ++ * <i>Offset: 830h</i> */ ++ volatile uint32_t dtknqr3_dthrctl; ++ /** Device IN Token Queue Read Register-4 (Read Only). / ++ * Device IN EPs empty Inr. Mask Register (Read/Write) ++ * <i>Offset: 834h</i> */ ++ volatile uint32_t dtknqr4_fifoemptymsk; ++ /** Device Each Endpoint Interrupt Register (Read Only). / ++ * <i>Offset: 838h</i> */ ++ volatile uint32_t deachint; ++ /** Device Each Endpoint Interrupt mask Register (Read/Write). / ++ * <i>Offset: 83Ch</i> */ ++ volatile uint32_t deachintmsk; ++ /** Device Each In Endpoint Interrupt mask Register (Read/Write). / ++ * <i>Offset: 840h</i> */ ++ volatile uint32_t diepeachintmsk[MAX_EPS_CHANNELS]; ++ /** Device Each Out Endpoint Interrupt mask Register (Read/Write). / ++ * <i>Offset: 880h</i> */ ++ volatile uint32_t doepeachintmsk[MAX_EPS_CHANNELS]; ++} dwc_otg_device_global_regs_t; ++ ++/** ++ * This union represents the bit fields in the Device Configuration ++ * Register. Read the register into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. Write the ++ * <i>d32</i> member to the dcfg register. ++ */ ++typedef union dcfg_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Device Speed */ ++ unsigned devspd:2; ++ /** Non Zero Length Status OUT Handshake */ ++ unsigned nzstsouthshk:1; ++#define DWC_DCFG_SEND_STALL 1 ++ ++ unsigned reserved3:1; ++ /** Device Addresses */ ++ unsigned devaddr:7; ++ /** Periodic Frame Interval */ ++ unsigned perfrint:2; ++#define DWC_DCFG_FRAME_INTERVAL_80 0 ++#define DWC_DCFG_FRAME_INTERVAL_85 1 ++#define DWC_DCFG_FRAME_INTERVAL_90 2 ++#define DWC_DCFG_FRAME_INTERVAL_95 3 ++ ++ unsigned reserved13_17:5; ++ /** In Endpoint Mis-match count */ ++ unsigned epmscnt:5; ++ /** Enable Descriptor DMA in Device mode */ ++ unsigned descdma:1; ++ } b; ++} dcfg_data_t; ++ ++/** ++ * This union represents the bit fields in the Device Control ++ * Register. Read the register into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. ++ */ ++typedef union dctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Remote Wakeup */ ++ unsigned rmtwkupsig:1; ++ /** Soft Disconnect */ ++ unsigned sftdiscon:1; ++ /** Global Non-Periodic IN NAK Status */ ++ unsigned gnpinnaksts:1; ++ /** Global OUT NAK Status */ ++ unsigned goutnaksts:1; ++ /** Test Control */ ++ unsigned tstctl:3; ++ /** Set Global Non-Periodic IN NAK */ ++ unsigned sgnpinnak:1; ++ /** Clear Global Non-Periodic IN NAK */ ++ unsigned cgnpinnak:1; ++ /** Set Global OUT NAK */ ++ unsigned sgoutnak:1; ++ /** Clear Global OUT NAK */ ++ unsigned cgoutnak:1; ++ ++ /** Power-On Programming Done */ ++ unsigned pwronprgdone:1; ++ /** Global Continue on BNA */ ++ unsigned gcontbna:1; ++ /** Global Multi Count */ ++ unsigned gmc:2; ++ /** Ignore Frame Number for ISOC EPs */ ++ unsigned ifrmnum:1; ++ /** NAK on Babble */ ++ unsigned nakonbble:1; ++ ++ unsigned reserved17_31:15; ++ } b; ++} dctl_data_t; ++ ++/** ++ * This union represents the bit fields in the Device Status ++ * Register. Read the register into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. ++ */ ++typedef union dsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Suspend Status */ ++ unsigned suspsts:1; ++ /** Enumerated Speed */ ++ unsigned enumspd:2; ++#define DWC_DSTS_ENUMSPD_HS_PHY_30MHZ_OR_60MHZ 0 ++#define DWC_DSTS_ENUMSPD_FS_PHY_30MHZ_OR_60MHZ 1 ++#define DWC_DSTS_ENUMSPD_LS_PHY_6MHZ 2 ++#define DWC_DSTS_ENUMSPD_FS_PHY_48MHZ 3 ++ /** Erratic Error */ ++ unsigned errticerr:1; ++ unsigned reserved4_7:4; ++ /** Frame or Microframe Number of the received SOF */ ++ unsigned soffn:14; ++ unsigned reserved22_31:10; ++ } b; ++} dsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Device IN EP Interrupt ++ * Register and the Device IN EP Common Mask Register. ++ * ++ * - Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union diepint_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Transfer complete mask */ ++ unsigned xfercompl:1; ++ /** Endpoint disable mask */ ++ unsigned epdisabled:1; ++ /** AHB Error mask */ ++ unsigned ahberr:1; ++ /** TimeOUT Handshake mask (non-ISOC EPs) */ ++ unsigned timeout:1; ++ /** IN Token received with TxF Empty mask */ ++ unsigned intktxfemp:1; ++ /** IN Token Received with EP mismatch mask */ ++ unsigned intknepmis:1; ++ /** IN Endpoint HAK Effective mask */ ++ unsigned inepnakeff:1; ++ /** IN Endpoint HAK Effective mask */ ++ unsigned emptyintr:1; ++ ++ unsigned txfifoundrn:1; ++ ++ /** BNA Interrupt mask */ ++ unsigned bna:1; ++ ++ unsigned reserved10_12:3; ++ /** BNA Interrupt mask */ ++ unsigned nak:1; ++ ++ unsigned reserved14_31:18; ++ } b; ++} diepint_data_t; ++ ++/** ++ * This union represents the bit fields in the Device IN EP ++ * Common/Dedicated Interrupt Mask Register. ++ */ ++typedef union diepint_data diepmsk_data_t; ++ ++/** ++ * This union represents the bit fields in the Device OUT EP Interrupt ++ * Registerand Device OUT EP Common Interrupt Mask Register. ++ * ++ * - Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union doepint_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Transfer complete */ ++ unsigned xfercompl:1; ++ /** Endpoint disable */ ++ unsigned epdisabled:1; ++ /** AHB Error */ ++ unsigned ahberr:1; ++ /** Setup Phase Done (contorl EPs) */ ++ unsigned setup:1; ++ /** OUT Token Received when Endpoint Disabled */ ++ unsigned outtknepdis:1; ++ ++ unsigned stsphsercvd:1; ++ /** Back-to-Back SETUP Packets Received */ ++ unsigned back2backsetup:1; ++ ++ unsigned reserved7:1; ++ /** OUT packet Error */ ++ unsigned outpkterr:1; ++ /** BNA Interrupt */ ++ unsigned bna:1; ++ ++ unsigned reserved10:1; ++ /** Packet Drop Status */ ++ unsigned pktdrpsts:1; ++ /** Babble Interrupt */ ++ unsigned babble:1; ++ /** NAK Interrupt */ ++ unsigned nak:1; ++ /** NYET Interrupt */ ++ unsigned nyet:1; ++ ++ unsigned reserved15_31:17; ++ } b; ++} doepint_data_t; ++ ++/** ++ * This union represents the bit fields in the Device OUT EP ++ * Common/Dedicated Interrupt Mask Register. ++ */ ++typedef union doepint_data doepmsk_data_t; ++ ++/** ++ * This union represents the bit fields in the Device All EP Interrupt ++ * and Mask Registers. ++ * - Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union daint_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** IN Endpoint bits */ ++ unsigned in:16; ++ /** OUT Endpoint bits */ ++ unsigned out:16; ++ } ep; ++ struct { ++ /** IN Endpoint bits */ ++ unsigned inep0:1; ++ unsigned inep1:1; ++ unsigned inep2:1; ++ unsigned inep3:1; ++ unsigned inep4:1; ++ unsigned inep5:1; ++ unsigned inep6:1; ++ unsigned inep7:1; ++ unsigned inep8:1; ++ unsigned inep9:1; ++ unsigned inep10:1; ++ unsigned inep11:1; ++ unsigned inep12:1; ++ unsigned inep13:1; ++ unsigned inep14:1; ++ unsigned inep15:1; ++ /** OUT Endpoint bits */ ++ unsigned outep0:1; ++ unsigned outep1:1; ++ unsigned outep2:1; ++ unsigned outep3:1; ++ unsigned outep4:1; ++ unsigned outep5:1; ++ unsigned outep6:1; ++ unsigned outep7:1; ++ unsigned outep8:1; ++ unsigned outep9:1; ++ unsigned outep10:1; ++ unsigned outep11:1; ++ unsigned outep12:1; ++ unsigned outep13:1; ++ unsigned outep14:1; ++ unsigned outep15:1; ++ } b; ++} daint_data_t; ++ ++/** ++ * This union represents the bit fields in the Device IN Token Queue ++ * Read Registers. ++ * - Read the register into the <i>d32</i> member. ++ * - READ-ONLY Register ++ */ ++typedef union dtknq1_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** In Token Queue Write Pointer */ ++ unsigned intknwptr:5; ++ /** Reserved */ ++ unsigned reserved05_06:2; ++ /** write pointer has wrapped. */ ++ unsigned wrap_bit:1; ++ /** EP Numbers of IN Tokens 0 ... 4 */ ++ unsigned epnums0_5:24; ++ } b; ++} dtknq1_data_t; ++ ++/** ++ * This union represents Threshold control Register ++ * - Read and write the register into the <i>d32</i> member. ++ * - READ-WRITABLE Register ++ */ ++typedef union dthrctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** non ISO Tx Thr. Enable */ ++ unsigned non_iso_thr_en:1; ++ /** ISO Tx Thr. Enable */ ++ unsigned iso_thr_en:1; ++ /** Tx Thr. Length */ ++ unsigned tx_thr_len:9; ++ /** AHB Threshold ratio */ ++ unsigned ahb_thr_ratio:2; ++ /** Reserved */ ++ unsigned reserved13_15:3; ++ /** Rx Thr. Enable */ ++ unsigned rx_thr_en:1; ++ /** Rx Thr. Length */ ++ unsigned rx_thr_len:9; ++ /** Reserved */ ++ unsigned reserved26_31:6; ++ } b; ++} dthrctl_data_t; ++ ++/** ++ * Device Logical IN Endpoint-Specific Registers. <i>Offsets ++ * 900h-AFCh</i> ++ * ++ * There will be one set of endpoint registers per logical endpoint ++ * implemented. ++ * ++ * <i>These registers are visible only in Device mode and must not be ++ * accessed in Host mode, as the results are unknown.</i> ++ */ ++typedef struct dwc_otg_dev_in_ep_regs { ++ /** Device IN Endpoint Control Register. <i>Offset:900h + ++ * (ep_num * 20h) + 00h</i> */ ++ volatile uint32_t diepctl; ++ /** Reserved. <i>Offset:900h + (ep_num * 20h) + 04h</i> */ ++ uint32_t reserved04; ++ /** Device IN Endpoint Interrupt Register. <i>Offset:900h + ++ * (ep_num * 20h) + 08h</i> */ ++ volatile uint32_t diepint; ++ /** Reserved. <i>Offset:900h + (ep_num * 20h) + 0Ch</i> */ ++ uint32_t reserved0C; ++ /** Device IN Endpoint Transfer Size ++ * Register. <i>Offset:900h + (ep_num * 20h) + 10h</i> */ ++ volatile uint32_t dieptsiz; ++ /** Device IN Endpoint DMA Address Register. <i>Offset:900h + ++ * (ep_num * 20h) + 14h</i> */ ++ volatile uint32_t diepdma; ++ /** Device IN Endpoint Transmit FIFO Status Register. <i>Offset:900h + ++ * (ep_num * 20h) + 18h</i> */ ++ volatile uint32_t dtxfsts; ++ /** Device IN Endpoint DMA Buffer Register. <i>Offset:900h + ++ * (ep_num * 20h) + 1Ch</i> */ ++ volatile uint32_t diepdmab; ++} dwc_otg_dev_in_ep_regs_t; ++ ++/** ++ * Device Logical OUT Endpoint-Specific Registers. <i>Offsets: ++ * B00h-CFCh</i> ++ * ++ * There will be one set of endpoint registers per logical endpoint ++ * implemented. ++ * ++ * <i>These registers are visible only in Device mode and must not be ++ * accessed in Host mode, as the results are unknown.</i> ++ */ ++typedef struct dwc_otg_dev_out_ep_regs { ++ /** Device OUT Endpoint Control Register. <i>Offset:B00h + ++ * (ep_num * 20h) + 00h</i> */ ++ volatile uint32_t doepctl; ++ /** Device OUT Endpoint Frame number Register. <i>Offset: ++ * B00h + (ep_num * 20h) + 04h</i> */ ++ volatile uint32_t doepfn; ++ /** Device OUT Endpoint Interrupt Register. <i>Offset:B00h + ++ * (ep_num * 20h) + 08h</i> */ ++ volatile uint32_t doepint; ++ /** Reserved. <i>Offset:B00h + (ep_num * 20h) + 0Ch</i> */ ++ uint32_t reserved0C; ++ /** Device OUT Endpoint Transfer Size Register. <i>Offset: ++ * B00h + (ep_num * 20h) + 10h</i> */ ++ volatile uint32_t doeptsiz; ++ /** Device OUT Endpoint DMA Address Register. <i>Offset:B00h ++ * + (ep_num * 20h) + 14h</i> */ ++ volatile uint32_t doepdma; ++ /** Reserved. <i>Offset:B00h + * (ep_num * 20h) + 18h</i> */ ++ uint32_t unused; ++ /** Device OUT Endpoint DMA Buffer Register. <i>Offset:B00h ++ * + (ep_num * 20h) + 1Ch</i> */ ++ uint32_t doepdmab; ++} dwc_otg_dev_out_ep_regs_t; ++ ++/** ++ * This union represents the bit fields in the Device EP Control ++ * Register. Read the register into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. ++ */ ++typedef union depctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Maximum Packet Size ++ * IN/OUT EPn ++ * IN/OUT EP0 - 2 bits ++ * 2'b00: 64 Bytes ++ * 2'b01: 32 ++ * 2'b10: 16 ++ * 2'b11: 8 */ ++ unsigned mps:11; ++#define DWC_DEP0CTL_MPS_64 0 ++#define DWC_DEP0CTL_MPS_32 1 ++#define DWC_DEP0CTL_MPS_16 2 ++#define DWC_DEP0CTL_MPS_8 3 ++ ++ /** Next Endpoint ++ * IN EPn/IN EP0 ++ * OUT EPn/OUT EP0 - reserved */ ++ unsigned nextep:4; ++ ++ /** USB Active Endpoint */ ++ unsigned usbactep:1; ++ ++ /** Endpoint DPID (INTR/Bulk IN and OUT endpoints) ++ * This field contains the PID of the packet going to ++ * be received or transmitted on this endpoint. The ++ * application should program the PID of the first ++ * packet going to be received or transmitted on this ++ * endpoint , after the endpoint is ++ * activated. Application use the SetD1PID and ++ * SetD0PID fields of this register to program either ++ * D0 or D1 PID. ++ * ++ * The encoding for this field is ++ * - 0: D0 ++ * - 1: D1 ++ */ ++ unsigned dpid:1; ++ ++ /** NAK Status */ ++ unsigned naksts:1; ++ ++ /** Endpoint Type ++ * 2'b00: Control ++ * 2'b01: Isochronous ++ * 2'b10: Bulk ++ * 2'b11: Interrupt */ ++ unsigned eptype:2; ++ ++ /** Snoop Mode ++ * OUT EPn/OUT EP0 ++ * IN EPn/IN EP0 - reserved */ ++ unsigned snp:1; ++ ++ /** Stall Handshake */ ++ unsigned stall:1; ++ ++ /** Tx Fifo Number ++ * IN EPn/IN EP0 ++ * OUT EPn/OUT EP0 - reserved */ ++ unsigned txfnum:4; ++ ++ /** Clear NAK */ ++ unsigned cnak:1; ++ /** Set NAK */ ++ unsigned snak:1; ++ /** Set DATA0 PID (INTR/Bulk IN and OUT endpoints) ++ * Writing to this field sets the Endpoint DPID (DPID) ++ * field in this register to DATA0. Set Even ++ * (micro)frame (SetEvenFr) (ISO IN and OUT Endpoints) ++ * Writing to this field sets the Even/Odd ++ * (micro)frame (EO_FrNum) field to even (micro) ++ * frame. ++ */ ++ unsigned setd0pid:1; ++ /** Set DATA1 PID (INTR/Bulk IN and OUT endpoints) ++ * Writing to this field sets the Endpoint DPID (DPID) ++ * field in this register to DATA1 Set Odd ++ * (micro)frame (SetOddFr) (ISO IN and OUT Endpoints) ++ * Writing to this field sets the Even/Odd ++ * (micro)frame (EO_FrNum) field to odd (micro) frame. ++ */ ++ unsigned setd1pid:1; ++ ++ /** Endpoint Disable */ ++ unsigned epdis:1; ++ /** Endpoint Enable */ ++ unsigned epena:1; ++ } b; ++} depctl_data_t; ++ ++/** ++ * This union represents the bit fields in the Device EP Transfer ++ * Size Register. Read the register into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. ++ */ ++typedef union deptsiz_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Transfer size */ ++ unsigned xfersize:19; ++ /** Packet Count */ ++ unsigned pktcnt:10; ++ /** Multi Count - Periodic IN endpoints */ ++ unsigned mc:2; ++ unsigned reserved:1; ++ } b; ++} deptsiz_data_t; ++ ++/** ++ * This union represents the bit fields in the Device EP 0 Transfer ++ * Size Register. Read the register into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. ++ */ ++typedef union deptsiz0_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ /** Transfer size */ ++ unsigned xfersize:7; ++ /** Reserved */ ++ unsigned reserved7_18:12; ++ /** Packet Count */ ++ unsigned pktcnt:1; ++ /** Reserved */ ++ unsigned reserved20_28:9; ++ /**Setup Packet Count (DOEPTSIZ0 Only) */ ++ unsigned supcnt:2; ++ unsigned reserved31; ++ } b; ++} deptsiz0_data_t; ++ ++///////////////////////////////////////////////// ++// DMA Descriptor Specific Structures ++// ++ ++/** Buffer status definitions */ ++ ++#define BS_HOST_READY 0x0 ++#define BS_DMA_BUSY 0x1 ++#define BS_DMA_DONE 0x2 ++#define BS_HOST_BUSY 0x3 ++ ++/** Receive/Transmit status definitions */ ++ ++#define RTS_SUCCESS 0x0 ++#define RTS_BUFFLUSH 0x1 ++#define RTS_RESERVED 0x2 ++#define RTS_BUFERR 0x3 ++ ++/** ++ * This union represents the bit fields in the DMA Descriptor ++ * status quadlet. Read the quadlet into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it, <i>b_iso_out</i> and ++ * <i>b_iso_in</i> elements. ++ */ ++typedef union dev_dma_desc_sts { ++ /** raw register data */ ++ uint32_t d32; ++ /** quadlet bits */ ++ struct { ++ /** Received number of bytes */ ++ unsigned bytes:16; ++ ++ unsigned reserved16_22:7; ++ /** Multiple Transfer - only for OUT EPs */ ++ unsigned mtrf:1; ++ /** Setup Packet received - only for OUT EPs */ ++ unsigned sr:1; ++ /** Interrupt On Complete */ ++ unsigned ioc:1; ++ /** Short Packet */ ++ unsigned sp:1; ++ /** Last */ ++ unsigned l:1; ++ /** Receive Status */ ++ unsigned sts:2; ++ /** Buffer Status */ ++ unsigned bs:2; ++ } b; ++ ++#ifdef DWC_EN_ISOC ++ /** iso out quadlet bits */ ++ struct { ++ /** Received number of bytes */ ++ unsigned rxbytes:11; ++ ++ unsigned reserved11:1; ++ /** Frame Number */ ++ unsigned framenum:11; ++ /** Received ISO Data PID */ ++ unsigned pid:2; ++ /** Interrupt On Complete */ ++ unsigned ioc:1; ++ /** Short Packet */ ++ unsigned sp:1; ++ /** Last */ ++ unsigned l:1; ++ /** Receive Status */ ++ unsigned rxsts:2; ++ /** Buffer Status */ ++ unsigned bs:2; ++ } b_iso_out; ++ ++ /** iso in quadlet bits */ ++ struct { ++ /** Transmited number of bytes */ ++ unsigned txbytes:12; ++ /** Frame Number */ ++ unsigned framenum:11; ++ /** Transmited ISO Data PID */ ++ unsigned pid:2; ++ /** Interrupt On Complete */ ++ unsigned ioc:1; ++ /** Short Packet */ ++ unsigned sp:1; ++ /** Last */ ++ unsigned l:1; ++ /** Transmit Status */ ++ unsigned txsts:2; ++ /** Buffer Status */ ++ unsigned bs:2; ++ } b_iso_in; ++#endif /* DWC_EN_ISOC */ ++} dev_dma_desc_sts_t; ++ ++/** ++ * DMA Descriptor structure ++ * ++ * DMA Descriptor structure contains two quadlets: ++ * Status quadlet and Data buffer pointer. ++ */ ++typedef struct dwc_otg_dev_dma_desc { ++ /** DMA Descriptor status quadlet */ ++ dev_dma_desc_sts_t status; ++ /** DMA Descriptor data buffer pointer */ ++ uint32_t buf; ++} dwc_otg_dev_dma_desc_t; ++ ++/** ++ * The dwc_otg_dev_if structure contains information needed to manage ++ * the DWC_otg controller acting in device mode. It represents the ++ * programming view of the device-specific aspects of the controller. ++ */ ++typedef struct dwc_otg_dev_if { ++ /** Pointer to device Global registers. ++ * Device Global Registers starting at offset 800h ++ */ ++ dwc_otg_device_global_regs_t *dev_global_regs; ++#define DWC_DEV_GLOBAL_REG_OFFSET 0x800 ++ ++ /** ++ * Device Logical IN Endpoint-Specific Registers 900h-AFCh ++ */ ++ dwc_otg_dev_in_ep_regs_t *in_ep_regs[MAX_EPS_CHANNELS]; ++#define DWC_DEV_IN_EP_REG_OFFSET 0x900 ++#define DWC_EP_REG_OFFSET 0x20 ++ ++ /** Device Logical OUT Endpoint-Specific Registers B00h-CFCh */ ++ dwc_otg_dev_out_ep_regs_t *out_ep_regs[MAX_EPS_CHANNELS]; ++#define DWC_DEV_OUT_EP_REG_OFFSET 0xB00 ++ ++ /* Device configuration information */ ++ uint8_t speed; /**< Device Speed 0: Unknown, 1: LS, 2:FS, 3: HS */ ++ uint8_t num_in_eps; /**< Number # of Tx EP range: 0-15 exept ep0 */ ++ uint8_t num_out_eps; /**< Number # of Rx EP range: 0-15 exept ep 0*/ ++ ++ /** Size of periodic FIFOs (Bytes) */ ++ uint16_t perio_tx_fifo_size[MAX_PERIO_FIFOS]; ++ ++ /** Size of Tx FIFOs (Bytes) */ ++ uint16_t tx_fifo_size[MAX_TX_FIFOS]; ++ ++ /** Thresholding enable flags and length varaiables **/ ++ uint16_t rx_thr_en; ++ uint16_t iso_tx_thr_en; ++ uint16_t non_iso_tx_thr_en; ++ ++ uint16_t rx_thr_length; ++ uint16_t tx_thr_length; ++ ++ /** ++ * Pointers to the DMA Descriptors for EP0 Control ++ * transfers (virtual and physical) ++ */ ++ ++ /** 2 descriptors for SETUP packets */ ++ dwc_dma_t dma_setup_desc_addr[2]; ++ dwc_otg_dev_dma_desc_t *setup_desc_addr[2]; ++ ++ /** Pointer to Descriptor with latest SETUP packet */ ++ dwc_otg_dev_dma_desc_t *psetup; ++ ++ /** Index of current SETUP handler descriptor */ ++ uint32_t setup_desc_index; ++ ++ /** Descriptor for Data In or Status In phases */ ++ dwc_dma_t dma_in_desc_addr; ++ dwc_otg_dev_dma_desc_t *in_desc_addr; ++ ++ /** Descriptor for Data Out or Status Out phases */ ++ dwc_dma_t dma_out_desc_addr; ++ dwc_otg_dev_dma_desc_t *out_desc_addr; ++ ++ /** Setup Packet Detected - if set clear NAK when queueing */ ++ uint32_t spd; ++ ++} dwc_otg_dev_if_t; ++ ++///////////////////////////////////////////////// ++// Host Mode Register Structures ++// ++/** ++ * The Host Global Registers structure defines the size and relative ++ * field offsets for the Host Mode Global Registers. Host Global ++ * Registers offsets 400h-7FFh. ++*/ ++typedef struct dwc_otg_host_global_regs { ++ /** Host Configuration Register. <i>Offset: 400h</i> */ ++ volatile uint32_t hcfg; ++ /** Host Frame Interval Register. <i>Offset: 404h</i> */ ++ volatile uint32_t hfir; ++ /** Host Frame Number / Frame Remaining Register. <i>Offset: 408h</i> */ ++ volatile uint32_t hfnum; ++ /** Reserved. <i>Offset: 40Ch</i> */ ++ uint32_t reserved40C; ++ /** Host Periodic Transmit FIFO/ Queue Status Register. <i>Offset: 410h</i> */ ++ volatile uint32_t hptxsts; ++ /** Host All Channels Interrupt Register. <i>Offset: 414h</i> */ ++ volatile uint32_t haint; ++ /** Host All Channels Interrupt Mask Register. <i>Offset: 418h</i> */ ++ volatile uint32_t haintmsk; ++ /** Host Frame List Base Address Register . <i>Offset: 41Ch</i> */ ++ volatile uint32_t hflbaddr; ++} dwc_otg_host_global_regs_t; ++ ++ ++/** ++ * This union represents the bit fields in the Host Configuration Register. ++ * Read the register into the <i>d32</i> member then set/clear the bits using ++ * the <i>b</i>it elements. Write the <i>d32</i> member to the hcfg register. ++ */ ++typedef union hcfg_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct ++ { ++ /** FS/LS Phy Clock Select */ ++ unsigned fslspclksel:2; ++#define DWC_HCFG_30_60_MHZ 0 ++#define DWC_HCFG_48_MHZ 1 ++#define DWC_HCFG_6_MHZ 2 ++ ++ /** FS/LS Only Support */ ++ unsigned fslssupp:1; ++ unsigned reserved3_22 : 20; ++ /** Enable Scatter/gather DMA in Host mode */ ++ unsigned descdma : 1; ++ /** Frame List Entries */ ++ unsigned frlisten: 2; ++ /** Enable Periodic Scheduling */ ++ unsigned perschedena: 1; ++ /** Periodic Scheduling Enabled Status */ ++ unsigned perschedstat: 1; ++ } b; ++} hcfg_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Frame Remaing/Number ++ * Register. ++ */ ++typedef union hfir_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ unsigned frint:16; ++ unsigned reserved:16; ++ } b; ++} hfir_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Frame Remaing/Number ++ * Register. ++ */ ++typedef union hfnum_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ unsigned frnum:16; ++#define DWC_HFNUM_MAX_FRNUM 0x3FFF ++ unsigned frrem:16; ++ } b; ++} hfnum_data_t; ++ ++typedef union hptxsts_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ unsigned ptxfspcavail:16; ++ unsigned ptxqspcavail:8; ++ /** Top of the Periodic Transmit Request Queue ++ * - bit 24 - Terminate (last entry for the selected channel) ++ * - bits 26:25 - Token Type ++ * - 2'b00 - Zero length ++ * - 2'b01 - Ping ++ * - 2'b10 - Disable ++ * - bits 30:27 - Channel Number ++ * - bit 31 - Odd/even microframe ++ */ ++ unsigned ptxqtop_terminate:1; ++ unsigned ptxqtop_token:2; ++ unsigned ptxqtop_chnum:4; ++ unsigned ptxqtop_odd:1; ++ } b; ++} hptxsts_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Port Control and Status ++ * Register. Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the ++ * hprt0 register. ++ */ ++typedef union hprt0_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned prtconnsts:1; ++ unsigned prtconndet:1; ++ unsigned prtena:1; ++ unsigned prtenchng:1; ++ unsigned prtovrcurract:1; ++ unsigned prtovrcurrchng:1; ++ unsigned prtres:1; ++ unsigned prtsusp:1; ++ unsigned prtrst:1; ++ unsigned reserved9:1; ++ unsigned prtlnsts:2; ++ unsigned prtpwr:1; ++ unsigned prttstctl:4; ++ unsigned prtspd:2; ++#define DWC_HPRT0_PRTSPD_HIGH_SPEED 0 ++#define DWC_HPRT0_PRTSPD_FULL_SPEED 1 ++#define DWC_HPRT0_PRTSPD_LOW_SPEED 2 ++ unsigned reserved19_31:13; ++ } b; ++} hprt0_data_t; ++ ++/** ++ * This union represents the bit fields in the Host All Interrupt ++ * Register. ++ */ ++typedef union haint_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned ch0:1; ++ unsigned ch1:1; ++ unsigned ch2:1; ++ unsigned ch3:1; ++ unsigned ch4:1; ++ unsigned ch5:1; ++ unsigned ch6:1; ++ unsigned ch7:1; ++ unsigned ch8:1; ++ unsigned ch9:1; ++ unsigned ch10:1; ++ unsigned ch11:1; ++ unsigned ch12:1; ++ unsigned ch13:1; ++ unsigned ch14:1; ++ unsigned ch15:1; ++ unsigned reserved:16; ++ } b; ++ ++ struct { ++ unsigned chint:16; ++ unsigned reserved:16; ++ } b2; ++} haint_data_t; ++ ++/** ++ * This union represents the bit fields in the Host All Interrupt ++ * Register. ++ */ ++typedef union haintmsk_data { ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct { ++ unsigned ch0:1; ++ unsigned ch1:1; ++ unsigned ch2:1; ++ unsigned ch3:1; ++ unsigned ch4:1; ++ unsigned ch5:1; ++ unsigned ch6:1; ++ unsigned ch7:1; ++ unsigned ch8:1; ++ unsigned ch9:1; ++ unsigned ch10:1; ++ unsigned ch11:1; ++ unsigned ch12:1; ++ unsigned ch13:1; ++ unsigned ch14:1; ++ unsigned ch15:1; ++ unsigned reserved:16; ++ } b; ++ ++ struct { ++ unsigned chint:16; ++ unsigned reserved:16; ++ } b2; ++} haintmsk_data_t; ++ ++/** ++ * Host Channel Specific Registers. <i>500h-5FCh</i> ++ */ ++typedef struct dwc_otg_hc_regs ++{ ++ /** Host Channel 0 Characteristic Register. <i>Offset: 500h + (chan_num * 20h) + 00h</i> */ ++ volatile uint32_t hcchar; ++ /** Host Channel 0 Split Control Register. <i>Offset: 500h + (chan_num * 20h) + 04h</i> */ ++ volatile uint32_t hcsplt; ++ /** Host Channel 0 Interrupt Register. <i>Offset: 500h + (chan_num * 20h) + 08h</i> */ ++ volatile uint32_t hcint; ++ /** Host Channel 0 Interrupt Mask Register. <i>Offset: 500h + (chan_num * 20h) + 0Ch</i> */ ++ volatile uint32_t hcintmsk; ++ /** Host Channel 0 Transfer Size Register. <i>Offset: 500h + (chan_num * 20h) + 10h</i> */ ++ volatile uint32_t hctsiz; ++ /** Host Channel 0 DMA Address Register. <i>Offset: 500h + (chan_num * 20h) + 14h</i> */ ++ volatile uint32_t hcdma; ++ volatile uint32_t reserved; ++ /** Host Channel 0 DMA Buffer Address Register. <i>Offset: 500h + (chan_num * 20h) + 1Ch</i> */ ++ volatile uint32_t hcdmab; ++} dwc_otg_hc_regs_t; ++ ++/** ++ * This union represents the bit fields in the Host Channel Characteristics ++ * Register. Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the ++ * hcchar register. ++ */ ++typedef union hcchar_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** Maximum packet size in bytes */ ++ unsigned mps:11; ++ ++ /** Endpoint number */ ++ unsigned epnum:4; ++ ++ /** 0: OUT, 1: IN */ ++ unsigned epdir:1; ++ ++ unsigned reserved:1; ++ ++ /** 0: Full/high speed device, 1: Low speed device */ ++ unsigned lspddev:1; ++ ++ /** 0: Control, 1: Isoc, 2: Bulk, 3: Intr */ ++ unsigned eptype:2; ++ ++ /** Packets per frame for periodic transfers. 0 is reserved. */ ++ unsigned multicnt:2; ++ ++ /** Device address */ ++ unsigned devaddr:7; ++ ++ /** ++ * Frame to transmit periodic transaction. ++ * 0: even, 1: odd ++ */ ++ unsigned oddfrm:1; ++ ++ /** Channel disable */ ++ unsigned chdis:1; ++ ++ /** Channel enable */ ++ unsigned chen:1; ++ } b; ++} hcchar_data_t; ++ ++typedef union hcsplt_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** Port Address */ ++ unsigned prtaddr:7; ++ ++ /** Hub Address */ ++ unsigned hubaddr:7; ++ ++ /** Transaction Position */ ++ unsigned xactpos:2; ++#define DWC_HCSPLIT_XACTPOS_MID 0 ++#define DWC_HCSPLIT_XACTPOS_END 1 ++#define DWC_HCSPLIT_XACTPOS_BEGIN 2 ++#define DWC_HCSPLIT_XACTPOS_ALL 3 ++ ++ /** Do Complete Split */ ++ unsigned compsplt:1; ++ ++ /** Reserved */ ++ unsigned reserved:14; ++ ++ /** Split Enble */ ++ unsigned spltena:1; ++ } b; ++} hcsplt_data_t; ++ ++/** ++ * This union represents the bit fields in the Host All Interrupt ++ * Register. ++ */ ++typedef union hcint_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ /** Transfer Complete */ ++ unsigned xfercomp:1; ++ /** Channel Halted */ ++ unsigned chhltd:1; ++ /** AHB Error */ ++ unsigned ahberr:1; ++ /** STALL Response Received */ ++ unsigned stall:1; ++ /** NAK Response Received */ ++ unsigned nak:1; ++ /** ACK Response Received */ ++ unsigned ack:1; ++ /** NYET Response Received */ ++ unsigned nyet:1; ++ /** Transaction Err */ ++ unsigned xacterr:1; ++ /** Babble Error */ ++ unsigned bblerr:1; ++ /** Frame Overrun */ ++ unsigned frmovrun:1; ++ /** Data Toggle Error */ ++ unsigned datatglerr:1; ++ /** Buffer Not Available (only for DDMA mode) */ ++ unsigned bna : 1; ++ /** Exessive transaction error (only for DDMA mode) */ ++ unsigned xcs_xact : 1; ++ /** Frame List Rollover interrupt */ ++ unsigned frm_list_roll : 1; ++ /** Reserved */ ++ unsigned reserved14_31 : 18; ++ } b; ++} hcint_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Channel Interrupt Mask ++ * Register. Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the ++ * hcintmsk register. ++ */ ++typedef union hcintmsk_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct ++ { ++ unsigned xfercompl : 1; ++ unsigned chhltd : 1; ++ unsigned ahberr : 1; ++ unsigned stall : 1; ++ unsigned nak : 1; ++ unsigned ack : 1; ++ unsigned nyet : 1; ++ unsigned xacterr : 1; ++ unsigned bblerr : 1; ++ unsigned frmovrun : 1; ++ unsigned datatglerr : 1; ++ unsigned bna : 1; ++ unsigned xcs_xact : 1; ++ unsigned frm_list_roll : 1; ++ unsigned reserved14_31 : 18; ++ } b; ++} hcintmsk_data_t; ++ ++/** ++ * This union represents the bit fields in the Host Channel Transfer Size ++ * Register. Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. Write the <i>d32</i> member to the ++ * hcchar register. ++ */ ++ ++typedef union hctsiz_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct ++ { ++ /** Total transfer size in bytes */ ++ unsigned xfersize:19; ++ ++ /** Data packets to transfer */ ++ unsigned pktcnt:10; ++ ++ /** ++ * Packet ID for next data packet ++ * 0: DATA0 ++ * 1: DATA2 ++ * 2: DATA1 ++ * 3: MDATA (non-Control), SETUP (Control) ++ */ ++ unsigned pid:2; ++#define DWC_HCTSIZ_DATA0 0 ++#define DWC_HCTSIZ_DATA1 2 ++#define DWC_HCTSIZ_DATA2 1 ++#define DWC_HCTSIZ_MDATA 3 ++#define DWC_HCTSIZ_SETUP 3 ++ ++ /** Do PING protocol when 1 */ ++ unsigned dopng:1; ++ } b; ++ ++ /** register bits */ ++ struct ++ { ++ /** Scheduling information */ ++ unsigned schinfo : 8; ++ ++ /** Number of transfer descriptors. ++ * Max value: ++ * 64 in general, ++ * 256 only for HS isochronous endpoint. ++ */ ++ unsigned ntd : 8; ++ ++ /** Data packets to transfer */ ++ unsigned reserved16_28 : 13; ++ ++ /** ++ * Packet ID for next data packet ++ * 0: DATA0 ++ * 1: DATA2 ++ * 2: DATA1 ++ * 3: MDATA (non-Control) ++ */ ++ unsigned pid : 2; ++ ++ /** Do PING protocol when 1 */ ++ unsigned dopng : 1; ++ } b_ddma; ++} hctsiz_data_t; ++ ++ ++/** ++ * This union represents the bit fields in the Host DMA Address ++ * Register used in Descriptor DMA mode. ++ */ ++typedef union hcdma_data ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** register bits */ ++ struct ++ { ++ unsigned reserved0_2 : 3; ++ /** Current Transfer Descriptor. Not used for ISOC */ ++ unsigned ctd : 8; ++ /** Start Address of Descriptor List */ ++ unsigned dma_addr : 21; ++ } b; ++} hcdma_data_t; ++ ++/** ++ * This union represents the bit fields in the DMA Descriptor ++ * status quadlet for host mode. Read the quadlet into the <i>d32</i> member then ++ * set/clear the bits using the <i>b</i>it elements. ++ */ ++typedef union host_dma_desc_sts ++{ ++ /** raw register data */ ++ uint32_t d32; ++ /** quadlet bits */ ++ ++ /* for non-isochronous */ ++ struct { ++ /** Number of bytes */ ++ unsigned n_bytes : 17; ++ /** QTD offset to jump when Short Packet received - only for IN EPs */ ++ unsigned qtd_offset : 6; ++ /** ++ * Set to request the core to jump to alternate QTD if ++ * Short Packet received - only for IN EPs ++ */ ++ unsigned a_qtd : 1; ++ /** ++ * Setup Packet bit. When set indicates that buffer contains ++ * setup packet. ++ */ ++ unsigned sup : 1; ++ /** Interrupt On Complete */ ++ unsigned ioc : 1; ++ /** End of List */ ++ unsigned eol : 1; ++ unsigned reserved27 : 1; ++ /** Rx/Tx Status */ ++ unsigned sts : 2; ++ #define DMA_DESC_STS_PKTERR 1 ++ unsigned reserved30 : 1; ++ /** Active Bit */ ++ unsigned a : 1; ++ } b; ++ /* for isochronous */ ++ struct { ++ /** Number of bytes */ ++ unsigned n_bytes : 12; ++ unsigned reserved12_24 : 13; ++ /** Interrupt On Complete */ ++ unsigned ioc : 1; ++ unsigned reserved26_27 : 2; ++ /** Rx/Tx Status */ ++ unsigned sts : 2; ++ unsigned reserved30 : 1; ++ /** Active Bit */ ++ unsigned a : 1; ++ } b_isoc; ++} host_dma_desc_sts_t; ++ ++#define MAX_DMA_DESC_SIZE 131071 ++#define MAX_DMA_DESC_NUM_GENERIC 64 ++#define MAX_DMA_DESC_NUM_HS_ISOC 256 ++#define MAX_FRLIST_EN_NUM 64 ++/** ++ * Host-mode DMA Descriptor structure ++ * ++ * DMA Descriptor structure contains two quadlets: ++ * Status quadlet and Data buffer pointer. ++ */ ++typedef struct dwc_otg_host_dma_desc ++{ ++ /** DMA Descriptor status quadlet */ ++ host_dma_desc_sts_t status; ++ /** DMA Descriptor data buffer pointer */ ++ uint32_t buf; ++} dwc_otg_host_dma_desc_t; ++ ++/** OTG Host Interface Structure. ++ * ++ * The OTG Host Interface Structure structure contains information ++ * needed to manage the DWC_otg controller acting in host mode. It ++ * represents the programming view of the host-specific aspects of the ++ * controller. ++ */ ++typedef struct dwc_otg_host_if { ++ /** Host Global Registers starting at offset 400h.*/ ++ dwc_otg_host_global_regs_t *host_global_regs; ++#define DWC_OTG_HOST_GLOBAL_REG_OFFSET 0x400 ++ ++ /** Host Port 0 Control and Status Register */ ++ volatile uint32_t *hprt0; ++#define DWC_OTG_HOST_PORT_REGS_OFFSET 0x440 ++ ++ /** Host Channel Specific Registers at offsets 500h-5FCh. */ ++ dwc_otg_hc_regs_t *hc_regs[MAX_EPS_CHANNELS]; ++#define DWC_OTG_HOST_CHAN_REGS_OFFSET 0x500 ++#define DWC_OTG_CHAN_REGS_OFFSET 0x20 ++ ++ /* Host configuration information */ ++ /** Number of Host Channels (range: 1-16) */ ++ uint8_t num_host_channels; ++ /** Periodic EPs supported (0: no, 1: yes) */ ++ uint8_t perio_eps_supported; ++ /** Periodic Tx FIFO Size (Only 1 host periodic Tx FIFO) */ ++ uint16_t perio_tx_fifo_size; ++ ++} dwc_otg_host_if_t; ++ ++/** ++ * This union represents the bit fields in the Power and Clock Gating Control ++ * Register. Read the register into the <i>d32</i> member then set/clear the ++ * bits using the <i>b</i>it elements. ++ */ ++typedef union pcgcctl_data { ++ /** raw register data */ ++ uint32_t d32; ++ ++ /** register bits */ ++ struct { ++ /** Stop Pclk */ ++ unsigned stoppclk:1; ++ /** Gate Hclk */ ++ unsigned gatehclk:1; ++ /** Power Clamp */ ++ unsigned pwrclmp:1; ++ /** Reset Power Down Modules */ ++ unsigned rstpdwnmodule:1; ++ /** PHY Suspended */ ++ unsigned physuspended:1; ++ /** Enable Sleep Clock Gating (Enbl_L1Gating) */ ++ unsigned enbl_sleep_gating:1; ++ /** PHY In Sleep (PhySleep) */ ++ unsigned phy_in_sleep:1; ++ /** Deep Sleep*/ ++ unsigned deep_sleep:1; ++ ++ unsigned reserved31_8:24; ++ } b; ++} pcgcctl_data_t; ++ ++#endif +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/test/Makefile +@@ -0,0 +1,16 @@ ++ ++PERL=/usr/bin/perl ++PL_TESTS=test_sysfs.pl test_mod_param.pl ++ ++.PHONY : test ++test : perl_tests ++ ++perl_tests : ++ @echo ++ @echo Running perl tests ++ @for test in $(PL_TESTS); do \ ++ if $(PERL) ./$$test ; then \ ++ echo "=======> $$test, PASSED" ; \ ++ else echo "=======> $$test, FAILED" ; \ ++ fi \ ++ done +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/test/dwc_otg_test.pm +@@ -0,0 +1,337 @@ ++package dwc_otg_test; ++ ++use strict; ++use Exporter (); ++ ++use vars qw(@ISA @EXPORT ++$sysfsdir $paramdir $errors $params ++); ++ ++@ISA = qw(Exporter); ++ ++# ++# Globals ++# ++$sysfsdir = "/sys/devices/lm0"; ++$paramdir = "/sys/module/dwc_otg"; ++$errors = 0; ++ ++$params = [ ++ { ++ NAME => "otg_cap", ++ DEFAULT => 0, ++ ENUM => [], ++ LOW => 0, ++ HIGH => 2 ++ }, ++ { ++ NAME => "dma_enable", ++ DEFAULT => 0, ++ ENUM => [], ++ LOW => 0, ++ HIGH => 1 ++ }, ++ { ++ NAME => "dma_burst_size", ++ DEFAULT => 32, ++ ENUM => [1, 4, 8, 16, 32, 64, 128, 256], ++ LOW => 1, ++ HIGH => 256 ++ }, ++ { ++ NAME => "host_speed", ++ DEFAULT => 0, ++ ENUM => [], ++ LOW => 0, ++ HIGH => 1 ++ }, ++ { ++ NAME => "host_support_fs_ls_low_power", ++ DEFAULT => 0, ++ ENUM => [], ++ LOW => 0, ++ HIGH => 1 ++ }, ++ { ++ NAME => "host_ls_low_power_phy_clk", ++ DEFAULT => 0, ++ ENUM => [], ++ LOW => 0, ++ HIGH => 1 ++ }, ++ { ++ NAME => "dev_speed", ++ DEFAULT => 0, ++ ENUM => [], ++ LOW => 0, ++ HIGH => 1 ++ }, ++ { ++ NAME => "enable_dynamic_fifo", ++ DEFAULT => 1, ++ ENUM => [], ++ LOW => 0, ++ HIGH => 1 ++ }, ++ { ++ NAME => "data_fifo_size", ++ DEFAULT => 8192, ++ ENUM => [], ++ LOW => 32, ++ HIGH => 32768 ++ }, ++ { ++ NAME => "dev_rx_fifo_size", ++ DEFAULT => 1064, ++ ENUM => [], ++ LOW => 16, ++ HIGH => 32768 ++ }, ++ { ++ NAME => "dev_nperio_tx_fifo_size", ++ DEFAULT => 1024, ++ ENUM => [], ++ LOW => 16, ++ HIGH => 32768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_1", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_2", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_3", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_4", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_5", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_6", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_7", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_8", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_9", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_10", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_11", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_12", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_13", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_14", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "dev_perio_tx_fifo_size_15", ++ DEFAULT => 256, ++ ENUM => [], ++ LOW => 4, ++ HIGH => 768 ++ }, ++ { ++ NAME => "host_rx_fifo_size", ++ DEFAULT => 1024, ++ ENUM => [], ++ LOW => 16, ++ HIGH => 32768 ++ }, ++ { ++ NAME => "host_nperio_tx_fifo_size", ++ DEFAULT => 1024, ++ ENUM => [], ++ LOW => 16, ++ HIGH => 32768 ++ }, ++ { ++ NAME => "host_perio_tx_fifo_size", ++ DEFAULT => 1024, ++ ENUM => [], ++ LOW => 16, ++ HIGH => 32768 ++ }, ++ { ++ NAME => "max_transfer_size", ++ DEFAULT => 65535, ++ ENUM => [], ++ LOW => 2047, ++ HIGH => 65535 ++ }, ++ { ++ NAME => "max_packet_count", ++ DEFAULT => 511, ++ ENUM => [], ++ LOW => 15, ++ HIGH => 511 ++ }, ++ { ++ NAME => "host_channels", ++ DEFAULT => 12, ++ ENUM => [], ++ LOW => 1, ++ HIGH => 16 ++ }, ++ { ++ NAME => "dev_endpoints", ++ DEFAULT => 6, ++ ENUM => [], ++ LOW => 1, ++ HIGH => 15 ++ }, ++ { ++ NAME => "phy_type", ++ DEFAULT => 1, ++ ENUM => [], ++ LOW => 0, ++ HIGH => 2 ++ }, ++ { ++ NAME => "phy_utmi_width", ++ DEFAULT => 16, ++ ENUM => [8, 16], ++ LOW => 8, ++ HIGH => 16 ++ }, ++ { ++ NAME => "phy_ulpi_ddr", ++ DEFAULT => 0, ++ ENUM => [], ++ LOW => 0, ++ HIGH => 1 ++ }, ++ ]; ++ ++ ++# ++# ++sub check_arch { ++ $_ = `uname -m`; ++ chomp; ++ unless (m/armv4tl/) { ++ warn "# \n# Can't execute on $_. Run on integrator platform.\n# \n"; ++ return 0; ++ } ++ return 1; ++} ++ ++# ++# ++sub load_module { ++ my $params = shift; ++ print "\nRemoving Module\n"; ++ system "rmmod dwc_otg"; ++ print "Loading Module\n"; ++ if ($params ne "") { ++ print "Module Parameters: $params\n"; ++ } ++ if (system("modprobe dwc_otg $params")) { ++ warn "Unable to load module\n"; ++ return 0; ++ } ++ return 1; ++} ++ ++# ++# ++sub test_status { ++ my $arg = shift; ++ ++ print "\n"; ++ ++ if (defined $arg) { ++ warn "WARNING: $arg\n"; ++ } ++ ++ if ($errors > 0) { ++ warn "TEST FAILED with $errors errors\n"; ++ return 0; ++ } else { ++ print "TEST PASSED\n"; ++ return 0 if (defined $arg); ++ } ++ return 1; ++} ++ ++# ++# ++@EXPORT = qw( ++$sysfsdir ++$paramdir ++$params ++$errors ++check_arch ++load_module ++test_status ++); ++ ++1; +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/test/test_mod_param.pl +@@ -0,0 +1,133 @@ ++#!/usr/bin/perl -w ++# ++# Run this program on the integrator. ++# ++# - Tests module parameter default values. ++# - Tests setting of valid module parameter values via modprobe. ++# - Tests invalid module parameter values. ++# ----------------------------------------------------------------------------- ++use strict; ++use dwc_otg_test; ++ ++check_arch() or die; ++ ++# ++# ++sub test { ++ my ($param,$expected) = @_; ++ my $value = get($param); ++ ++ if ($value == $expected) { ++ print "$param = $value, okay\n"; ++ } ++ ++ else { ++ warn "ERROR: value of $param != $expected, $value\n"; ++ $errors ++; ++ } ++} ++ ++# ++# ++sub get { ++ my $param = shift; ++ my $tmp = `cat $paramdir/$param`; ++ chomp $tmp; ++ return $tmp; ++} ++ ++# ++# ++sub test_main { ++ ++ print "\nTesting Module Parameters\n"; ++ ++ load_module("") or die; ++ ++ # Test initial values ++ print "\nTesting Default Values\n"; ++ foreach (@{$params}) { ++ test ($_->{NAME}, $_->{DEFAULT}); ++ } ++ ++ # Test low value ++ print "\nTesting Low Value\n"; ++ my $cmd_params = ""; ++ foreach (@{$params}) { ++ $cmd_params = $cmd_params . "$_->{NAME}=$_->{LOW} "; ++ } ++ load_module($cmd_params) or die; ++ ++ foreach (@{$params}) { ++ test ($_->{NAME}, $_->{LOW}); ++ } ++ ++ # Test high value ++ print "\nTesting High Value\n"; ++ $cmd_params = ""; ++ foreach (@{$params}) { ++ $cmd_params = $cmd_params . "$_->{NAME}=$_->{HIGH} "; ++ } ++ load_module($cmd_params) or die; ++ ++ foreach (@{$params}) { ++ test ($_->{NAME}, $_->{HIGH}); ++ } ++ ++ # Test Enum ++ print "\nTesting Enumerated\n"; ++ foreach (@{$params}) { ++ if (defined $_->{ENUM}) { ++ my $value; ++ foreach $value (@{$_->{ENUM}}) { ++ $cmd_params = "$_->{NAME}=$value"; ++ load_module($cmd_params) or die; ++ test ($_->{NAME}, $value); ++ } ++ } ++ } ++ ++ # Test Invalid Values ++ print "\nTesting Invalid Values\n"; ++ $cmd_params = ""; ++ foreach (@{$params}) { ++ $cmd_params = $cmd_params . sprintf "$_->{NAME}=%d ", $_->{LOW}-1; ++ } ++ load_module($cmd_params) or die; ++ ++ foreach (@{$params}) { ++ test ($_->{NAME}, $_->{DEFAULT}); ++ } ++ ++ $cmd_params = ""; ++ foreach (@{$params}) { ++ $cmd_params = $cmd_params . sprintf "$_->{NAME}=%d ", $_->{HIGH}+1; ++ } ++ load_module($cmd_params) or die; ++ ++ foreach (@{$params}) { ++ test ($_->{NAME}, $_->{DEFAULT}); ++ } ++ ++ print "\nTesting Enumerated\n"; ++ foreach (@{$params}) { ++ if (defined $_->{ENUM}) { ++ my $value; ++ foreach $value (@{$_->{ENUM}}) { ++ $value = $value + 1; ++ $cmd_params = "$_->{NAME}=$value"; ++ load_module($cmd_params) or die; ++ test ($_->{NAME}, $_->{DEFAULT}); ++ $value = $value - 2; ++ $cmd_params = "$_->{NAME}=$value"; ++ load_module($cmd_params) or die; ++ test ($_->{NAME}, $_->{DEFAULT}); ++ } ++ } ++ } ++ ++ test_status() or die; ++} ++ ++test_main(); ++0; +--- /dev/null ++++ b/drivers/usb/host/dwc_otg/test/test_sysfs.pl +@@ -0,0 +1,193 @@ ++#!/usr/bin/perl -w ++# ++# Run this program on the integrator ++# - Tests select sysfs attributes. ++# - Todo ... test more attributes, hnp/srp, buspower/bussuspend, etc. ++# ----------------------------------------------------------------------------- ++use strict; ++use dwc_otg_test; ++ ++check_arch() or die; ++ ++# ++# ++sub test { ++ my ($attr,$expected) = @_; ++ my $string = get($attr); ++ ++ if ($string eq $expected) { ++ printf("$attr = $string, okay\n"); ++ } ++ else { ++ warn "ERROR: value of $attr != $expected, $string\n"; ++ $errors ++; ++ } ++} ++ ++# ++# ++sub set { ++ my ($reg, $value) = @_; ++ system "echo $value > $sysfsdir/$reg"; ++} ++ ++# ++# ++sub get { ++ my $attr = shift; ++ my $string = `cat $sysfsdir/$attr`; ++ chomp $string; ++ if ($string =~ m/\s\=\s/) { ++ my $tmp; ++ ($tmp, $string) = split /\s=\s/, $string; ++ } ++ return $string; ++} ++ ++# ++# ++sub test_main { ++ print("\nTesting Sysfs Attributes\n"); ++ ++ load_module("") or die; ++ ++ # Test initial values of regoffset/regvalue/guid/gsnpsid ++ print("\nTesting Default Values\n"); ++ ++ test("regoffset", "0xffffffff"); ++ test("regvalue", "invalid offset"); ++ test("guid", "0x12345678"); # this will fail if it has been changed ++ test("gsnpsid", "0x4f54200a"); ++ ++ # Test operation of regoffset/regvalue ++ print("\nTesting regoffset\n"); ++ set('regoffset', '5a5a5a5a'); ++ test("regoffset", "0xffffffff"); ++ ++ set('regoffset', '0'); ++ test("regoffset", "0x00000000"); ++ ++ set('regoffset', '40000'); ++ test("regoffset", "0x00000000"); ++ ++ set('regoffset', '3ffff'); ++ test("regoffset", "0x0003ffff"); ++ ++ set('regoffset', '1'); ++ test("regoffset", "0x00000001"); ++ ++ print("\nTesting regvalue\n"); ++ set('regoffset', '3c'); ++ test("regvalue", "0x12345678"); ++ set('regvalue', '5a5a5a5a'); ++ test("regvalue", "0x5a5a5a5a"); ++ set('regvalue','a5a5a5a5'); ++ test("regvalue", "0xa5a5a5a5"); ++ set('guid','12345678'); ++ ++ # Test HNP Capable ++ print("\nTesting HNP Capable bit\n"); ++ set('hnpcapable', '1'); ++ test("hnpcapable", "0x1"); ++ set('hnpcapable','0'); ++ test("hnpcapable", "0x0"); ++ ++ set('regoffset','0c'); ++ ++ my $old = get('gusbcfg'); ++ print("setting hnpcapable\n"); ++ set('hnpcapable', '1'); ++ test("hnpcapable", "0x1"); ++ test('gusbcfg', sprintf "0x%08x", (oct ($old) | (1<<9))); ++ test('regvalue', sprintf "0x%08x", (oct ($old) | (1<<9))); ++ ++ $old = get('gusbcfg'); ++ print("clearing hnpcapable\n"); ++ set('hnpcapable', '0'); ++ test("hnpcapable", "0x0"); ++ test ('gusbcfg', sprintf "0x%08x", oct ($old) & (~(1<<9))); ++ test ('regvalue', sprintf "0x%08x", oct ($old) & (~(1<<9))); ++ ++ # Test SRP Capable ++ print("\nTesting SRP Capable bit\n"); ++ set('srpcapable', '1'); ++ test("srpcapable", "0x1"); ++ set('srpcapable','0'); ++ test("srpcapable", "0x0"); ++ ++ set('regoffset','0c'); ++ ++ $old = get('gusbcfg'); ++ print("setting srpcapable\n"); ++ set('srpcapable', '1'); ++ test("srpcapable", "0x1"); ++ test('gusbcfg', sprintf "0x%08x", (oct ($old) | (1<<8))); ++ test('regvalue', sprintf "0x%08x", (oct ($old) | (1<<8))); ++ ++ $old = get('gusbcfg'); ++ print("clearing srpcapable\n"); ++ set('srpcapable', '0'); ++ test("srpcapable", "0x0"); ++ test('gusbcfg', sprintf "0x%08x", oct ($old) & (~(1<<8))); ++ test('regvalue', sprintf "0x%08x", oct ($old) & (~(1<<8))); ++ ++ # Test GGPIO ++ print("\nTesting GGPIO\n"); ++ set('ggpio','5a5a5a5a'); ++ test('ggpio','0x5a5a0000'); ++ set('ggpio','a5a5a5a5'); ++ test('ggpio','0xa5a50000'); ++ set('ggpio','11110000'); ++ test('ggpio','0x11110000'); ++ set('ggpio','00001111'); ++ test('ggpio','0x00000000'); ++ ++ # Test DEVSPEED ++ print("\nTesting DEVSPEED\n"); ++ set('regoffset','800'); ++ $old = get('regvalue'); ++ set('devspeed','0'); ++ test('devspeed','0x0'); ++ test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3))); ++ set('devspeed','1'); ++ test('devspeed','0x1'); ++ test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3) | 1)); ++ set('devspeed','2'); ++ test('devspeed','0x2'); ++ test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3) | 2)); ++ set('devspeed','3'); ++ test('devspeed','0x3'); ++ test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3) | 3)); ++ set('devspeed','4'); ++ test('devspeed','0x0'); ++ test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3))); ++ set('devspeed','5'); ++ test('devspeed','0x1'); ++ test('regvalue',sprintf("0x%08x", oct($old) & ~(0x3) | 1)); ++ ++ ++ # mode Returns the current mode:0 for device mode1 for host mode Read ++ # hnp Initiate the Host Negotiation Protocol. Read returns the status. Read/Write ++ # srp Initiate the Session Request Protocol. Read returns the status. Read/Write ++ # buspower Get or Set the Power State of the bus (0 - Off or 1 - On) Read/Write ++ # bussuspend Suspend the USB bus. Read/Write ++ # busconnected Get the connection status of the bus Read ++ ++ # gotgctl Get or set the Core Control Status Register. Read/Write ++ ## gusbcfg Get or set the Core USB Configuration Register Read/Write ++ # grxfsiz Get or set the Receive FIFO Size Register Read/Write ++ # gnptxfsiz Get or set the non-periodic Transmit Size Register Read/Write ++ # gpvndctl Get or set the PHY Vendor Control Register Read/Write ++ ## ggpio Get the value in the lower 16-bits of the General Purpose IO Register or Set the upper 16 bits. Read/Write ++ ## guid Get or set the value of the User ID Register Read/Write ++ ## gsnpsid Get the value of the Synopsys ID Regester Read ++ ## devspeed Get or set the device speed setting in the DCFG register Read/Write ++ # enumspeed Gets the device enumeration Speed. Read ++ # hptxfsiz Get the value of the Host Periodic Transmit FIFO Read ++ # hprt0 Get or Set the value in the Host Port Control and Status Register Read/Write ++ ++ test_status("TEST NYI") or die; ++} ++ ++test_main(); ++0; diff --git a/target/linux/brcm2708/patches-3.3/0002-Main-bcm2708-linux-port.patch b/target/linux/brcm2708/patches-3.3/0002-Main-bcm2708-linux-port.patch new file mode 100644 index 0000000000..78de767b1e --- /dev/null +++ b/target/linux/brcm2708/patches-3.3/0002-Main-bcm2708-linux-port.patch @@ -0,0 +1,11023 @@ +From d5ef856b4628a4531c1627dec100eeff15e8b31e Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Tue, 17 Jan 2012 19:20:11 +0000 +Subject: [PATCH 2/7] Main bcm2708 linux port + +Signed-off-by: popcornmix <popcornmix@gmail.com> +--- + arch/arm/Kconfig | 14 + + arch/arm/Makefile | 1 + + arch/arm/boot/compressed/Makefile | 8 +- + arch/arm/boot/compressed/divdi3.c | 338 +++++ + arch/arm/boot/compressed/longlong.h | 151 ++ + arch/arm/configs/bcmrpi_cutdown_defconfig | 1558 ++++++++++++++++++++ + arch/arm/kernel/armksyms.c | 4 + + arch/arm/lib/Makefile | 3 +- + arch/arm/lib/divdi3.c | 338 +++++ + arch/arm/lib/lib1funcs.S | 27 + + arch/arm/lib/longlong.h | 151 ++ + arch/arm/mach-bcm2708/Kconfig | 25 + + arch/arm/mach-bcm2708/Makefile | 8 + + arch/arm/mach-bcm2708/Makefile.boot | 3 + + arch/arm/mach-bcm2708/armctrl.c | 399 +++++ + arch/arm/mach-bcm2708/armctrl.h | 27 + + arch/arm/mach-bcm2708/bcm2708.c | 658 +++++++++ + arch/arm/mach-bcm2708/bcm2708.h | 51 + + arch/arm/mach-bcm2708/bcm2708_gpio.c | 323 ++++ + arch/arm/mach-bcm2708/clock.c | 61 + + arch/arm/mach-bcm2708/clock.h | 24 + + arch/arm/mach-bcm2708/dma.c | 397 +++++ + arch/arm/mach-bcm2708/include/mach/arm_control.h | 419 ++++++ + arch/arm/mach-bcm2708/include/mach/arm_power.h | 60 + + arch/arm/mach-bcm2708/include/mach/clkdev.h | 7 + + arch/arm/mach-bcm2708/include/mach/debug-macro.S | 24 + + arch/arm/mach-bcm2708/include/mach/dma.h | 84 ++ + arch/arm/mach-bcm2708/include/mach/entry-macro.S | 69 + + arch/arm/mach-bcm2708/include/mach/frc.h | 38 + + arch/arm/mach-bcm2708/include/mach/gpio.h | 48 + + arch/arm/mach-bcm2708/include/mach/hardware.h | 28 + + arch/arm/mach-bcm2708/include/mach/io.h | 28 + + arch/arm/mach-bcm2708/include/mach/irqs.h | 190 +++ + arch/arm/mach-bcm2708/include/mach/irqs.h.orig | 185 +++ + arch/arm/mach-bcm2708/include/mach/memory.h | 59 + + arch/arm/mach-bcm2708/include/mach/platform.h | 210 +++ + arch/arm/mach-bcm2708/include/mach/platform.h.orig | 210 +++ + arch/arm/mach-bcm2708/include/mach/power.h | 26 + + arch/arm/mach-bcm2708/include/mach/system.h | 53 + + arch/arm/mach-bcm2708/include/mach/timex.h | 23 + + arch/arm/mach-bcm2708/include/mach/uncompress.h | 49 + + arch/arm/mach-bcm2708/include/mach/vc_mem.h | 34 + + arch/arm/mach-bcm2708/include/mach/vcio.h | 42 + + arch/arm/mach-bcm2708/include/mach/vmalloc.h | 20 + + arch/arm/mach-bcm2708/power.c | 193 +++ + arch/arm/mach-bcm2708/vc_mem.c | 467 ++++++ + arch/arm/mach-bcm2708/vcio.c | 309 ++++ + arch/arm/mm/Kconfig | 2 +- + arch/arm/mm/alignment.c | 6 +- + arch/arm/mm/proc-v6.S | 15 +- + drivers/mmc/host/Kconfig | 29 + + drivers/mmc/host/Makefile | 2 + + drivers/mmc/host/bcm2708_mci.c | 889 +++++++++++ + drivers/mmc/host/bcm2708_mci.h | 101 ++ + drivers/mmc/host/sdhci-bcm2708.c | 1461 ++++++++++++++++++ + drivers/mmc/host/sdhci.c | 623 ++++++--- + drivers/mmc/host/sdhci.h | 46 + + include/linux/mmc/host.h | 29 + + include/linux/mmc/sdhci.h | 10 + + 59 files changed, 10481 insertions(+), 176 deletions(-) + create mode 100644 arch/arm/boot/compressed/divdi3.c + create mode 100644 arch/arm/boot/compressed/longlong.h + create mode 100644 arch/arm/configs/bcmrpi_cutdown_defconfig + create mode 100644 arch/arm/lib/divdi3.c + create mode 100644 arch/arm/lib/longlong.h + create mode 100644 arch/arm/mach-bcm2708/Kconfig + create mode 100644 arch/arm/mach-bcm2708/Makefile + create mode 100644 arch/arm/mach-bcm2708/Makefile.boot + create mode 100644 arch/arm/mach-bcm2708/armctrl.c + create mode 100644 arch/arm/mach-bcm2708/armctrl.h + create mode 100644 arch/arm/mach-bcm2708/bcm2708.c + create mode 100644 arch/arm/mach-bcm2708/bcm2708.h + create mode 100644 arch/arm/mach-bcm2708/bcm2708_gpio.c + create mode 100644 arch/arm/mach-bcm2708/clock.c + create mode 100644 arch/arm/mach-bcm2708/clock.h + create mode 100644 arch/arm/mach-bcm2708/dma.c + create mode 100644 arch/arm/mach-bcm2708/include/mach/arm_control.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/arm_power.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/clkdev.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/debug-macro.S + create mode 100644 arch/arm/mach-bcm2708/include/mach/dma.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/entry-macro.S + create mode 100644 arch/arm/mach-bcm2708/include/mach/frc.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/gpio.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/hardware.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/io.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/irqs.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/irqs.h.orig + create mode 100644 arch/arm/mach-bcm2708/include/mach/memory.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/platform.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/platform.h.orig + create mode 100644 arch/arm/mach-bcm2708/include/mach/power.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/system.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/timex.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/uncompress.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/vc_mem.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/vcio.h + create mode 100644 arch/arm/mach-bcm2708/include/mach/vmalloc.h + create mode 100644 arch/arm/mach-bcm2708/power.c + create mode 100644 arch/arm/mach-bcm2708/vc_mem.c + create mode 100644 arch/arm/mach-bcm2708/vcio.c + create mode 100644 drivers/mmc/host/bcm2708_mci.c + create mode 100644 drivers/mmc/host/bcm2708_mci.h + create mode 100644 drivers/mmc/host/sdhci-bcm2708.c + +--- a/arch/arm/Kconfig ++++ b/arch/arm/Kconfig +@@ -961,6 +961,20 @@ config PLAT_SPEAR + help + Support for ST's SPEAr platform (SPEAr3xx, SPEAr6xx and SPEAr13xx). + ++config ARCH_BCM2708 ++ bool "Broadcom BCM2708 family" ++ select CPU_V6 ++ select ARM_AMBA ++ select HAVE_CLK ++ select CLKDEV_LOOKUP ++ select GENERIC_CLOCKEVENTS ++ select ARM_ERRATA_411920 ++ select MACH_BCM2708 ++ select VC4 ++ select NEED_MACH_MEMORY_H ++ help ++ This enables support for Broadcom BCM2708 boards. ++ + config ARCH_VT8500 + bool "VIA/WonderMedia 85xx" + select CPU_ARM926T +@@ -1103,6 +1117,7 @@ source "arch/arm/plat-versatile/Kconfig" + source "arch/arm/mach-vt8500/Kconfig" + + source "arch/arm/mach-w90x900/Kconfig" ++source "arch/arm/mach-bcm2708/Kconfig" + + # Definitions to make life easier + config ARCH_ACORN +--- a/arch/arm/Makefile ++++ b/arch/arm/Makefile +@@ -196,6 +196,7 @@ machine-$(CONFIG_MACH_SPEAR310) := spea + machine-$(CONFIG_MACH_SPEAR320) := spear3xx + machine-$(CONFIG_MACH_SPEAR600) := spear6xx + machine-$(CONFIG_ARCH_ZYNQ) := zynq ++machine-$(CONFIG_ARCH_BCM2708) := bcm2708 + + # Platform directory name. This list is sorted alphanumerically + # by CONFIG_* macro name. +--- a/arch/arm/boot/compressed/Makefile ++++ b/arch/arm/boot/compressed/Makefile +@@ -147,11 +147,17 @@ LDFLAGS_vmlinux += -X + LDFLAGS_vmlinux += -T + + # For __aeabi_uidivmod +-lib1funcs = $(obj)/lib1funcs.o ++lib1funcs = $(obj)/lib1funcs.o $(obj)/divdi3.o + + $(obj)/lib1funcs.S: $(srctree)/arch/$(SRCARCH)/lib/lib1funcs.S + $(call cmd,shipped) + ++$(obj)/longlong.h: $(srctree)/arch/$(SRCARCH)/lib/longlong.h FORCE ++ $(call cmd,shipped) ++ ++$(obj)/divdi3.c: $(srctree)/arch/$(SRCARCH)/lib/divdi3.c $(obj)/longlong.h FORCE ++ $(call cmd,shipped) ++ + # We need to prevent any GOTOFF relocs being used with references + # to symbols in the .bss section since we cannot relocate them + # independently from the rest at run time. This can be achieved by +--- /dev/null ++++ b/arch/arm/boot/compressed/divdi3.c +@@ -0,0 +1,338 @@ ++/* 64-bit multiplication and division ++ Copyright (C) 1989, 1992-1999, 2000, 2001, 2002, 2003 ++ Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++#include "longlong.h" ++ ++#define W_TYPE_SIZE (sizeof(long)) ++ ++#ifdef __ARMEB__ ++struct DWstruct { long high, low;}; ++#else ++struct DWstruct { long low, high;}; ++#endif ++ ++typedef union { struct DWstruct s; long long ll; } DWunion; ++ ++/* Prototypes of exported functions. */ ++long long __divdi3 (long long u, long long v); ++long long __moddi3 (long long u, long long v); ++unsigned long long __udivdi3 (unsigned long long u, unsigned long long v); ++unsigned long long __umoddi3 (unsigned long long u, unsigned long long v); ++ ++static unsigned long long ++__udivmoddi4 (unsigned long long n, unsigned long long d, unsigned long long *rp) ++{ ++ DWunion ww; ++ DWunion nn, dd; ++ DWunion rr; ++ unsigned long d0, d1, n0, n1, n2; ++ unsigned long q0, q1; ++ unsigned long b, bm; ++ ++ nn.ll = n; ++ dd.ll = d; ++ ++ d0 = dd.s.low; ++ d1 = dd.s.high; ++ n0 = nn.s.low; ++ n1 = nn.s.high; ++ ++#if !UDIV_NEEDS_NORMALIZATION ++ if (d1 == 0) ++ { ++ if (d0 > n1) ++ { ++ /* 0q = nn / 0D */ ++ ++ udiv_qrnnd (q0, n0, n1, n0, d0); ++ q1 = 0; ++ ++ /* Remainder in n0. */ ++ } ++ else ++ { ++ /* qq = NN / 0d */ ++ ++ if (d0 == 0) ++ d0 = 1 / d0; /* Divide intentionally by zero. */ ++ ++ udiv_qrnnd (q1, n1, 0, n1, d0); ++ udiv_qrnnd (q0, n0, n1, n0, d0); ++ ++ /* Remainder in n0. */ ++ } ++ ++ if (rp != 0) ++ { ++ rr.s.low = n0; ++ rr.s.high = 0; ++ *rp = rr.ll; ++ } ++ } ++ ++#else /* UDIV_NEEDS_NORMALIZATION */ ++ ++ if (d1 == 0) ++ { ++ if (d0 > n1) ++ { ++ /* 0q = nn / 0D */ ++ ++ count_leading_zeros (bm, d0); ++ ++ if (bm != 0) ++ { ++ /* Normalize, i.e. make the most significant bit of the ++ denominator set. */ ++ ++ d0 = d0 << bm; ++ n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm)); ++ n0 = n0 << bm; ++ } ++ ++ udiv_qrnnd (q0, n0, n1, n0, d0); ++ q1 = 0; ++ ++ /* Remainder in n0 >> bm. */ ++ } ++ else ++ { ++ /* qq = NN / 0d */ ++ ++ if (d0 == 0) ++ d0 = 1 / d0; /* Divide intentionally by zero. */ ++ ++ count_leading_zeros (bm, d0); ++ ++ if (bm == 0) ++ { ++ /* From (n1 >= d0) /\ (the most significant bit of d0 is set), ++ conclude (the most significant bit of n1 is set) /\ (the ++ leading quotient digit q1 = 1). ++ ++ This special case is necessary, not an optimization. ++ (Shifts counts of W_TYPE_SIZE are undefined.) */ ++ ++ n1 -= d0; ++ q1 = 1; ++ } ++ else ++ { ++ /* Normalize. */ ++ ++ b = W_TYPE_SIZE - bm; ++ ++ d0 = d0 << bm; ++ n2 = n1 >> b; ++ n1 = (n1 << bm) | (n0 >> b); ++ n0 = n0 << bm; ++ ++ udiv_qrnnd (q1, n1, n2, n1, d0); ++ } ++ ++ /* n1 != d0... */ ++ ++ udiv_qrnnd (q0, n0, n1, n0, d0); ++ ++ /* Remainder in n0 >> bm. */ ++ } ++ ++ if (rp != 0) ++ { ++ rr.s.low = n0 >> bm; ++ rr.s.high = 0; ++ *rp = rr.ll; ++ } ++ } ++#endif /* UDIV_NEEDS_NORMALIZATION */ ++ ++ else ++ { ++ if (d1 > n1) ++ { ++ /* 00 = nn / DD */ ++ ++ q0 = 0; ++ q1 = 0; ++ ++ /* Remainder in n1n0. */ ++ if (rp != 0) ++ { ++ rr.s.low = n0; ++ rr.s.high = n1; ++ *rp = rr.ll; ++ } ++ } ++ else ++ { ++ /* 0q = NN / dd */ ++ ++ count_leading_zeros (bm, d1); ++ if (bm == 0) ++ { ++ /* From (n1 >= d1) /\ (the most significant bit of d1 is set), ++ conclude (the most significant bit of n1 is set) /\ (the ++ quotient digit q0 = 0 or 1). ++ ++ This special case is necessary, not an optimization. */ ++ ++ /* The condition on the next line takes advantage of that ++ n1 >= d1 (true due to program flow). */ ++ if (n1 > d1 || n0 >= d0) ++ { ++ q0 = 1; ++ sub_ddmmss (n1, n0, n1, n0, d1, d0); ++ } ++ else ++ q0 = 0; ++ ++ q1 = 0; ++ ++ if (rp != 0) ++ { ++ rr.s.low = n0; ++ rr.s.high = n1; ++ *rp = rr.ll; ++ } ++ } ++ else ++ { ++ unsigned long m1, m0; ++ /* Normalize. */ ++ ++ b = W_TYPE_SIZE - bm; ++ ++ d1 = (d1 << bm) | (d0 >> b); ++ d0 = d0 << bm; ++ n2 = n1 >> b; ++ n1 = (n1 << bm) | (n0 >> b); ++ n0 = n0 << bm; ++ ++ udiv_qrnnd (q0, n1, n2, n1, d1); ++ umul_ppmm (m1, m0, q0, d0); ++ ++ if (m1 > n1 || (m1 == n1 && m0 > n0)) ++ { ++ q0--; ++ sub_ddmmss (m1, m0, m1, m0, d1, d0); ++ } ++ ++ q1 = 0; ++ ++ /* Remainder in (n1n0 - m1m0) >> bm. */ ++ if (rp != 0) ++ { ++ sub_ddmmss (n1, n0, n1, n0, m1, m0); ++ rr.s.low = (n1 << b) | (n0 >> bm); ++ rr.s.high = n1 >> bm; ++ *rp = rr.ll; ++ } ++ } ++ } ++ } ++ ++ ww.s.low = q0; ++ ww.s.high = q1; ++ return ww.ll; ++} ++ ++long long ++__divdi3 (long long u, long long v) ++{ ++ long c = 0; ++ long long w; ++ ++ if (u < 0) ++ { ++ c = ~c; ++ u = -u; ++ } ++ if (v < 0) ++ { ++ c = ~c; ++ v = -v; ++ } ++ w = __udivmoddi4 (u, v, 0); ++ if (c) ++ w = -w; ++ return w; ++} ++ ++long long ++__moddi3 (long long u, long long v) ++{ ++ long c = 0; ++ long long w; ++ ++ if (u < 0) ++ { ++ c = ~c; ++ u = -u; ++ } ++ if (v < 0) ++ v = -v; ++ __udivmoddi4 (u, v, &w); ++ if (c) ++ w = -w; ++ return w; ++} ++ ++unsigned long long ++__udivdi3 (unsigned long long u, unsigned long long v) ++{ ++ return __udivmoddi4 (u, v, 0); ++} ++ ++unsigned long long ++__umoddi3 (unsigned long long u, unsigned long long v) ++{ ++ unsigned long long w; ++ ++ __udivmoddi4 (u, v, &w); ++ return w; ++} ++ ++long long ++__gnu_ldivmod_helper (long long a, ++ ++ long long b, ++ long long *remainder) ++{ ++ long long quotient; ++ ++ quotient = __divdi3 (a, b); ++ *remainder = a - b * quotient; ++ ++ return quotient; ++} ++ ++unsigned long long ++ ++__gnu_uldivmod_helper (unsigned long long a, ++ ++ unsigned long long b, ++ unsigned long long *remainder) ++{ ++ unsigned long long quotient; ++ ++ quotient = __udivdi3 (a, b); ++ *remainder = a - b * quotient; ++ return quotient; ++} +--- /dev/null ++++ b/arch/arm/boot/compressed/longlong.h +@@ -0,0 +1,151 @@ ++/* longlong.h -- based on code from gcc-2.95.3 ++ ++ definitions for mixed size 32/64 bit arithmetic. ++ Copyright (C) 1991, 92, 94, 95, 96, 1997, 1998 Free Software Foundation, Inc. ++ ++ This definition file 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, or (at your option) any later version. ++ ++ This definition file is distributed in the hope that it will be ++ useful, but WITHOUT ANY WARRANTY; without even the implied ++ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ See the GNU General Public License for more details. ++ ++ 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., 59 Temple Place - Suite 330, ++ Boston, MA 02111-1307, USA. */ ++ ++/* Borrowed from GCC 2.95.3, I Molton 29/07/01 */ ++ ++#define USItype unsigned long ++#define SI_TYPE_SIZE sizeof(USItype) ++ ++#define __BITS4 (SI_TYPE_SIZE / 4) ++#define __ll_B (1L << (SI_TYPE_SIZE / 2)) ++#define __ll_lowpart(t) ((USItype) (t) % __ll_B) ++#define __ll_highpart(t) ((USItype) (t) / __ll_B) ++ ++/* Define auxiliary asm macros. ++ ++ 1) umul_ppmm(high_prod, low_prod, multipler, multiplicand) ++ multiplies two USItype integers MULTIPLER and MULTIPLICAND, ++ and generates a two-part USItype product in HIGH_PROD and ++ LOW_PROD. ++ ++ 2) __umulsidi3(a,b) multiplies two USItype integers A and B, ++ and returns a UDItype product. This is just a variant of umul_ppmm. ++ ++ 3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator, ++ denominator) divides a two-word unsigned integer, composed by the ++ integers HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and ++ places the quotient in QUOTIENT and the remainder in REMAINDER. ++ HIGH_NUMERATOR must be less than DENOMINATOR for correct operation. ++ If, in addition, the most significant bit of DENOMINATOR must be 1, ++ then the pre-processor symbol UDIV_NEEDS_NORMALIZATION is defined to 1. ++ ++ 4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator, ++ denominator). Like udiv_qrnnd but the numbers are signed. The ++ quotient is rounded towards 0. ++ ++ 5) count_leading_zeros(count, x) counts the number of zero-bits from ++ the msb to the first non-zero bit. This is the number of steps X ++ needs to be shifted left to set the msb. Undefined for X == 0. ++ ++ 6) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, ++ high_addend_2, low_addend_2) adds two two-word unsigned integers, ++ composed by HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and ++ LOW_ADDEND_2 respectively. The result is placed in HIGH_SUM and ++ LOW_SUM. Overflow (i.e. carry out) is not stored anywhere, and is ++ lost. ++ ++ 7) sub_ddmmss(high_difference, low_difference, high_minuend, ++ low_minuend, high_subtrahend, low_subtrahend) subtracts two ++ two-word unsigned integers, composed by HIGH_MINUEND_1 and ++ LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and LOW_SUBTRAHEND_2 ++ respectively. The result is placed in HIGH_DIFFERENCE and ++ LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere, ++ and is lost. ++ ++ If any of these macros are left undefined for a particular CPU, ++ C macros are used. */ ++ ++#if defined (__arm__) ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("adds %1, %4, %5 \n\ ++ adc %0, %2, %3" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "%r" ((USItype) (ah)), \ ++ "rI" ((USItype) (bh)), \ ++ "%r" ((USItype) (al)), \ ++ "rI" ((USItype) (bl))) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("subs %1, %4, %5 \n\ ++ sbc %0, %2, %3" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "r" ((USItype) (ah)), \ ++ "rI" ((USItype) (bh)), \ ++ "r" ((USItype) (al)), \ ++ "rI" ((USItype) (bl))) ++#define umul_ppmm(xh, xl, a, b) \ ++ __asm__ ("%@ Inlined umul_ppmm\n" \ ++ "umull %r1, %r0, %r2, %r3" \ ++ : "=&r" ((USItype)(xh)), \ ++ "=r" ((USItype)(xl)) \ ++ : "r" ((USItype)(a)), \ ++ "r" ((USItype)(b)) \ ++ : "r0", "r1") ++#define count_leading_zeros(count, x) \ ++ __asm__ ("clz %0, %1" : "=r"(count) : "r"(x)) ++#define UMUL_TIME 20 ++#define UDIV_TIME 100 ++#endif /* __arm__ */ ++ ++#define __umulsidi3(u, v) \ ++ ({DIunion __w; \ ++ umul_ppmm (__w.s.high, __w.s.low, u, v); \ ++ __w.ll; }) ++ ++#define __udiv_qrnnd_c(q, r, n1, n0, d) \ ++ do { \ ++ USItype __d1, __d0, __q1, __q0; \ ++ USItype __r1, __r0, __m; \ ++ __d1 = __ll_highpart (d); \ ++ __d0 = __ll_lowpart (d); \ ++ \ ++ __r1 = (n1) % __d1; \ ++ __q1 = (n1) / __d1; \ ++ __m = (USItype) __q1 * __d0; \ ++ __r1 = __r1 * __ll_B | __ll_highpart (n0); \ ++ if (__r1 < __m) \ ++ { \ ++ __q1--, __r1 += (d); \ ++ if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\ ++ if (__r1 < __m) \ ++ __q1--, __r1 += (d); \ ++ } \ ++ __r1 -= __m; \ ++ \ ++ __r0 = __r1 % __d1; \ ++ __q0 = __r1 / __d1; \ ++ __m = (USItype) __q0 * __d0; \ ++ __r0 = __r0 * __ll_B | __ll_lowpart (n0); \ ++ if (__r0 < __m) \ ++ { \ ++ __q0--, __r0 += (d); \ ++ if (__r0 >= (d)) \ ++ if (__r0 < __m) \ ++ __q0--, __r0 += (d); \ ++ } \ ++ __r0 -= __m; \ ++ \ ++ (q) = (USItype) __q1 * __ll_B | __q0; \ ++ (r) = __r0; \ ++ } while (0) ++ ++#define UDIV_NEEDS_NORMALIZATION 1 ++#define udiv_qrnnd __udiv_qrnnd_c +--- /dev/null ++++ b/arch/arm/configs/bcmrpi_cutdown_defconfig +@@ -0,0 +1,1558 @@ ++# ++# Automatically generated file; DO NOT EDIT. ++# Linux/arm 3.1.9 Kernel Configuration ++# ++CONFIG_ARM=y ++CONFIG_SYS_SUPPORTS_APM_EMULATION=y ++CONFIG_GENERIC_GPIO=y ++# CONFIG_ARCH_USES_GETTIMEOFFSET is not set ++CONFIG_GENERIC_CLOCKEVENTS=y ++CONFIG_KTIME_SCALAR=y ++CONFIG_HAVE_PROC_CPU=y ++CONFIG_STACKTRACE_SUPPORT=y ++CONFIG_HAVE_LATENCYTOP_SUPPORT=y ++CONFIG_LOCKDEP_SUPPORT=y ++CONFIG_TRACE_IRQFLAGS_SUPPORT=y ++CONFIG_HARDIRQS_SW_RESEND=y ++CONFIG_GENERIC_IRQ_PROBE=y ++CONFIG_RWSEM_GENERIC_SPINLOCK=y ++CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y ++CONFIG_GENERIC_HWEIGHT=y ++CONFIG_GENERIC_CALIBRATE_DELAY=y ++CONFIG_NEED_DMA_MAP_STATE=y ++CONFIG_VECTORS_BASE=0xffff0000 ++# CONFIG_ARM_PATCH_PHYS_VIRT is not set ++CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" ++CONFIG_HAVE_IRQ_WORK=y ++ ++# ++# General setup ++# ++CONFIG_EXPERIMENTAL=y ++CONFIG_BROKEN_ON_SMP=y ++CONFIG_INIT_ENV_ARG_LIMIT=32 ++CONFIG_CROSS_COMPILE="" ++CONFIG_LOCALVERSION="" ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_HAVE_KERNEL_GZIP=y ++CONFIG_HAVE_KERNEL_LZMA=y ++CONFIG_HAVE_KERNEL_LZO=y ++CONFIG_KERNEL_GZIP=y ++# CONFIG_KERNEL_LZMA is not set ++# CONFIG_KERNEL_LZO is not set ++CONFIG_DEFAULT_HOSTNAME="(none)" ++CONFIG_SWAP=y ++CONFIG_SYSVIPC=y ++CONFIG_SYSVIPC_SYSCTL=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_POSIX_MQUEUE_SYSCTL=y ++# CONFIG_BSD_PROCESS_ACCT is not set ++# CONFIG_FHANDLE is not set ++# CONFIG_TASKSTATS is not set ++# CONFIG_AUDIT is not set ++CONFIG_HAVE_GENERIC_HARDIRQS=y ++ ++# ++# IRQ subsystem ++# ++CONFIG_GENERIC_HARDIRQS=y ++CONFIG_HAVE_SPARSE_IRQ=y ++CONFIG_GENERIC_IRQ_SHOW=y ++# CONFIG_SPARSE_IRQ is not set ++ ++# ++# RCU Subsystem ++# ++CONFIG_TINY_RCU=y ++# CONFIG_PREEMPT_RCU is not set ++# CONFIG_RCU_TRACE is not set ++# CONFIG_TREE_RCU_TRACE is not set ++CONFIG_IKCONFIG=y ++CONFIG_IKCONFIG_PROC=y ++CONFIG_LOG_BUF_SHIFT=17 ++# CONFIG_CGROUPS is not set ++# CONFIG_NAMESPACES is not set ++# CONFIG_SCHED_AUTOGROUP is not set ++# CONFIG_SYSFS_DEPRECATED is not set ++# CONFIG_RELAY is not set ++# CONFIG_BLK_DEV_INITRD is not set ++# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set ++CONFIG_SYSCTL=y ++CONFIG_ANON_INODES=y ++CONFIG_EXPERT=y ++# CONFIG_UID16 is not set ++CONFIG_SYSCTL_SYSCALL=y ++# CONFIG_KALLSYMS is not set ++CONFIG_HOTPLUG=y ++CONFIG_PRINTK=y ++CONFIG_BUG=y ++# CONFIG_ELF_CORE is not set ++CONFIG_BASE_FULL=y ++CONFIG_FUTEX=y ++CONFIG_EPOLL=y ++CONFIG_SIGNALFD=y ++CONFIG_TIMERFD=y ++CONFIG_EVENTFD=y ++CONFIG_SHMEM=y ++CONFIG_AIO=y ++CONFIG_EMBEDDED=y ++CONFIG_HAVE_PERF_EVENTS=y ++CONFIG_PERF_USE_VMALLOC=y ++ ++# ++# Kernel Performance Events And Counters ++# ++# CONFIG_PERF_EVENTS is not set ++# CONFIG_PERF_COUNTERS is not set ++# CONFIG_VM_EVENT_COUNTERS is not set ++# CONFIG_COMPAT_BRK is not set ++CONFIG_SLAB=y ++# CONFIG_SLUB is not set ++# CONFIG_SLOB is not set ++# CONFIG_PROFILING is not set ++CONFIG_HAVE_OPROFILE=y ++# CONFIG_KPROBES is not set ++CONFIG_HAVE_KPROBES=y ++CONFIG_HAVE_KRETPROBES=y ++CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y ++CONFIG_HAVE_CLK=y ++CONFIG_HAVE_DMA_API_DEBUG=y ++ ++# ++# GCOV-based kernel profiling ++# ++CONFIG_HAVE_GENERIC_DMA_COHERENT=y ++CONFIG_SLABINFO=y ++CONFIG_RT_MUTEXES=y ++CONFIG_BASE_SMALL=0 ++CONFIG_MODULES=y ++# CONFIG_MODULE_FORCE_LOAD is not set ++CONFIG_MODULE_UNLOAD=y ++# CONFIG_MODULE_FORCE_UNLOAD is not set ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++CONFIG_BLOCK=y ++CONFIG_LBDAF=y ++# CONFIG_BLK_DEV_BSG is not set ++# CONFIG_BLK_DEV_BSGLIB is not set ++# CONFIG_BLK_DEV_INTEGRITY is not set ++ ++# ++# IO Schedulers ++# ++CONFIG_IOSCHED_NOOP=y ++CONFIG_IOSCHED_DEADLINE=y ++CONFIG_IOSCHED_CFQ=y ++# CONFIG_DEFAULT_DEADLINE is not set ++CONFIG_DEFAULT_CFQ=y ++# CONFIG_DEFAULT_NOOP is not set ++CONFIG_DEFAULT_IOSCHED="cfq" ++# CONFIG_INLINE_SPIN_TRYLOCK is not set ++# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK is not set ++# CONFIG_INLINE_SPIN_LOCK_BH is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQ is not set ++# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set ++CONFIG_INLINE_SPIN_UNLOCK=y ++# CONFIG_INLINE_SPIN_UNLOCK_BH is not set ++CONFIG_INLINE_SPIN_UNLOCK_IRQ=y ++# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_READ_TRYLOCK is not set ++# CONFIG_INLINE_READ_LOCK is not set ++# CONFIG_INLINE_READ_LOCK_BH is not set ++# CONFIG_INLINE_READ_LOCK_IRQ is not set ++# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set ++CONFIG_INLINE_READ_UNLOCK=y ++# CONFIG_INLINE_READ_UNLOCK_BH is not set ++CONFIG_INLINE_READ_UNLOCK_IRQ=y ++# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set ++# CONFIG_INLINE_WRITE_TRYLOCK is not set ++# CONFIG_INLINE_WRITE_LOCK is not set ++# CONFIG_INLINE_WRITE_LOCK_BH is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQ is not set ++# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set ++CONFIG_INLINE_WRITE_UNLOCK=y ++# CONFIG_INLINE_WRITE_UNLOCK_BH is not set ++CONFIG_INLINE_WRITE_UNLOCK_IRQ=y ++# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set ++# CONFIG_MUTEX_SPIN_ON_OWNER is not set ++CONFIG_FREEZER=y ++ ++# ++# System Type ++# ++CONFIG_MMU=y ++# CONFIG_ARCH_INTEGRATOR is not set ++# CONFIG_ARCH_REALVIEW is not set ++# CONFIG_ARCH_VERSATILE is not set ++# CONFIG_ARCH_VEXPRESS is not set ++# CONFIG_ARCH_AT91 is not set ++# CONFIG_ARCH_BCMRING is not set ++# CONFIG_ARCH_CLPS711X is not set ++# CONFIG_ARCH_CNS3XXX is not set ++# CONFIG_ARCH_GEMINI is not set ++# CONFIG_ARCH_PRIMA2 is not set ++# CONFIG_ARCH_EBSA110 is not set ++# CONFIG_ARCH_EP93XX is not set ++# CONFIG_ARCH_FOOTBRIDGE is not set ++# CONFIG_ARCH_MXC is not set ++# CONFIG_ARCH_MXS is not set ++# CONFIG_ARCH_NETX is not set ++# CONFIG_ARCH_H720X is not set ++# CONFIG_ARCH_IOP13XX is not set ++# CONFIG_ARCH_IOP32X is not set ++# CONFIG_ARCH_IOP33X is not set ++# CONFIG_ARCH_IXP23XX is not set ++# CONFIG_ARCH_IXP2000 is not set ++# CONFIG_ARCH_IXP4XX is not set ++# CONFIG_ARCH_DOVE is not set ++# CONFIG_ARCH_KIRKWOOD is not set ++# CONFIG_ARCH_LPC32XX is not set ++# CONFIG_ARCH_MV78XX0 is not set ++# CONFIG_ARCH_ORION5X is not set ++# CONFIG_ARCH_MMP is not set ++# CONFIG_ARCH_KS8695 is not set ++# CONFIG_ARCH_W90X900 is not set ++# CONFIG_ARCH_NUC93X is not set ++# CONFIG_ARCH_TEGRA is not set ++# CONFIG_ARCH_PNX4008 is not set ++# CONFIG_ARCH_PXA is not set ++# CONFIG_ARCH_MSM is not set ++# CONFIG_ARCH_SHMOBILE is not set ++# CONFIG_ARCH_RPC is not set ++# CONFIG_ARCH_SA1100 is not set ++# CONFIG_ARCH_S3C2410 is not set ++# CONFIG_ARCH_S3C64XX is not set ++# CONFIG_ARCH_S5P64X0 is not set ++# CONFIG_ARCH_S5PC100 is not set ++# CONFIG_ARCH_S5PV210 is not set ++# CONFIG_ARCH_EXYNOS4 is not set ++# CONFIG_ARCH_SHARK is not set ++# CONFIG_ARCH_TCC_926 is not set ++# CONFIG_ARCH_U300 is not set ++# CONFIG_ARCH_U8500 is not set ++# CONFIG_ARCH_NOMADIK is not set ++# CONFIG_ARCH_DAVINCI is not set ++# CONFIG_ARCH_OMAP is not set ++# CONFIG_PLAT_SPEAR is not set ++CONFIG_ARCH_BCM2708=y ++# CONFIG_ARCH_VT8500 is not set ++# CONFIG_ARCH_ZYNQ is not set ++ ++# ++# System MMU ++# ++ ++# ++# Broadcom BCM2708 Implementations ++# ++CONFIG_MACH_BCM2708=y ++CONFIG_BCM2708_GPIO=y ++CONFIG_BCM2708_VCMEM=y ++ ++# ++# Processor Type ++# ++CONFIG_CPU_V6=y ++CONFIG_CPU_32v6=y ++CONFIG_CPU_ABRT_EV6=y ++CONFIG_CPU_PABRT_V6=y ++CONFIG_CPU_CACHE_V6=y ++CONFIG_CPU_CACHE_VIPT=y ++CONFIG_CPU_COPY_V6=y ++CONFIG_CPU_TLB_V6=y ++CONFIG_CPU_HAS_ASID=y ++CONFIG_CPU_CP15=y ++CONFIG_CPU_CP15_MMU=y ++CONFIG_CPU_USE_DOMAINS=y ++ ++# ++# Processor Features ++# ++CONFIG_ARM_THUMB=y ++# CONFIG_CPU_ICACHE_DISABLE is not set ++# CONFIG_CPU_DCACHE_DISABLE is not set ++# CONFIG_CPU_BPREDICT_DISABLE is not set ++CONFIG_ARM_L1_CACHE_SHIFT=5 ++CONFIG_ARM_DMA_MEM_BUFFERABLE=y ++CONFIG_CPU_HAS_PMU=y ++CONFIG_ARM_ERRATA_411920=y ++# CONFIG_ARM_ERRATA_364296 is not set ++ ++# ++# Bus support ++# ++CONFIG_ARM_AMBA=y ++# CONFIG_PCI_SYSCALL is not set ++# CONFIG_ARCH_SUPPORTS_MSI is not set ++# CONFIG_PCCARD is not set ++ ++# ++# Kernel Features ++# ++CONFIG_TICK_ONESHOT=y ++CONFIG_NO_HZ=y ++# CONFIG_HIGH_RES_TIMERS is not set ++CONFIG_GENERIC_CLOCKEVENTS_BUILD=y ++CONFIG_VMSPLIT_3G=y ++# CONFIG_VMSPLIT_2G is not set ++# CONFIG_VMSPLIT_1G is not set ++CONFIG_PAGE_OFFSET=0xC0000000 ++CONFIG_PREEMPT_NONE=y ++# CONFIG_PREEMPT_VOLUNTARY is not set ++# CONFIG_PREEMPT is not set ++CONFIG_HZ=100 ++CONFIG_AEABI=y ++CONFIG_OABI_COMPAT=y ++# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set ++# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set ++CONFIG_HAVE_ARCH_PFN_VALID=y ++# CONFIG_HIGHMEM is not set ++CONFIG_SELECT_MEMORY_MODEL=y ++CONFIG_FLATMEM_MANUAL=y ++CONFIG_FLATMEM=y ++CONFIG_FLAT_NODE_MEM_MAP=y ++CONFIG_HAVE_MEMBLOCK=y ++CONFIG_PAGEFLAGS_EXTENDED=y ++CONFIG_SPLIT_PTLOCK_CPUS=4 ++# CONFIG_COMPACTION is not set ++# CONFIG_PHYS_ADDR_T_64BIT is not set ++CONFIG_ZONE_DMA_FLAG=0 ++CONFIG_VIRT_TO_BUS=y ++# CONFIG_KSM is not set ++CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 ++CONFIG_NEED_PER_CPU_KM=y ++# CONFIG_CLEANCACHE is not set ++CONFIG_FORCE_MAX_ZONEORDER=11 ++CONFIG_ALIGNMENT_TRAP=y ++# CONFIG_UACCESS_WITH_MEMCPY is not set ++# CONFIG_SECCOMP is not set ++# CONFIG_CC_STACKPROTECTOR is not set ++# CONFIG_DEPRECATED_PARAM_STRUCT is not set ++ ++# ++# Boot options ++# ++# CONFIG_USE_OF is not set ++CONFIG_ZBOOT_ROM_TEXT=0x0 ++CONFIG_ZBOOT_ROM_BSS=0x0 ++CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext3 rootwait" ++CONFIG_CMDLINE_FROM_BOOTLOADER=y ++# CONFIG_CMDLINE_EXTEND is not set ++# CONFIG_CMDLINE_FORCE is not set ++# CONFIG_XIP_KERNEL is not set ++# CONFIG_KEXEC is not set ++# CONFIG_CRASH_DUMP is not set ++# CONFIG_AUTO_ZRELADDR is not set ++ ++# ++# CPU Power Management ++# ++CONFIG_CPU_IDLE=y ++CONFIG_CPU_IDLE_GOV_LADDER=y ++CONFIG_CPU_IDLE_GOV_MENU=y ++ ++# ++# Floating point emulation ++# ++ ++# ++# At least one emulation must be selected ++# ++# CONFIG_FPE_NWFPE is not set ++# CONFIG_FPE_FASTFPE is not set ++CONFIG_VFP=y ++ ++# ++# Userspace binary formats ++# ++CONFIG_BINFMT_ELF=y ++CONFIG_HAVE_AOUT=y ++# CONFIG_BINFMT_AOUT is not set ++# CONFIG_BINFMT_MISC is not set ++ ++# ++# Power management options ++# ++CONFIG_SUSPEND=y ++CONFIG_SUSPEND_FREEZER=y ++CONFIG_PM_SLEEP=y ++# CONFIG_PM_RUNTIME is not set ++CONFIG_PM=y ++# CONFIG_PM_DEBUG is not set ++# CONFIG_APM_EMULATION is not set ++CONFIG_PM_CLK=y ++CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_NET=y ++ ++# ++# Networking options ++# ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_XFRM=y ++CONFIG_XFRM_USER=y ++# CONFIG_XFRM_SUB_POLICY is not set ++# CONFIG_XFRM_MIGRATE is not set ++# CONFIG_XFRM_STATISTICS is not set ++CONFIG_NET_KEY=m ++# CONFIG_NET_KEY_MIGRATE is not set ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++# CONFIG_IP_ADVANCED_ROUTER is not set ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++# CONFIG_IP_PNP_BOOTP is not set ++CONFIG_IP_PNP_RARP=y ++# CONFIG_NET_IPIP is not set ++# CONFIG_NET_IPGRE_DEMUX is not set ++# CONFIG_IP_MROUTE is not set ++# CONFIG_ARPD is not set ++CONFIG_SYN_COOKIES=y ++# CONFIG_INET_AH is not set ++# CONFIG_INET_ESP is not set ++# CONFIG_INET_IPCOMP is not set ++# CONFIG_INET_XFRM_TUNNEL is not set ++# CONFIG_INET_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++# CONFIG_INET_DIAG is not set ++# CONFIG_TCP_CONG_ADVANCED is not set ++CONFIG_TCP_CONG_CUBIC=y ++CONFIG_DEFAULT_TCP_CONG="cubic" ++# CONFIG_TCP_MD5SIG is not set ++# CONFIG_IPV6 is not set ++# CONFIG_NETWORK_SECMARK is not set ++# CONFIG_NETWORK_PHY_TIMESTAMPING is not set ++# CONFIG_NETFILTER is not set ++# CONFIG_IP_DCCP is not set ++# CONFIG_IP_SCTP is not set ++# CONFIG_RDS is not set ++# CONFIG_TIPC is not set ++# CONFIG_ATM is not set ++# CONFIG_L2TP is not set ++# CONFIG_BRIDGE is not set ++# CONFIG_NET_DSA is not set ++# CONFIG_VLAN_8021Q is not set ++# CONFIG_DECNET is not set ++# CONFIG_LLC2 is not set ++# CONFIG_IPX is not set ++# CONFIG_ATALK is not set ++# CONFIG_X25 is not set ++# CONFIG_LAPB is not set ++# CONFIG_ECONET is not set ++# CONFIG_WAN_ROUTER is not set ++# CONFIG_PHONET is not set ++# CONFIG_IEEE802154 is not set ++# CONFIG_NET_SCHED is not set ++# CONFIG_DCB is not set ++CONFIG_DNS_RESOLVER=y ++# CONFIG_BATMAN_ADV is not set ++ ++# ++# Network testing ++# ++CONFIG_NET_PKTGEN=m ++# CONFIG_HAMRADIO is not set ++# CONFIG_CAN is not set ++# CONFIG_IRDA is not set ++# CONFIG_BT is not set ++# CONFIG_AF_RXRPC is not set ++CONFIG_WIRELESS=y ++CONFIG_WEXT_CORE=y ++CONFIG_WEXT_PROC=y ++CONFIG_CFG80211=y ++# CONFIG_NL80211_TESTMODE is not set ++# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set ++# CONFIG_CFG80211_REG_DEBUG is not set ++CONFIG_CFG80211_DEFAULT_PS=y ++# CONFIG_CFG80211_INTERNAL_REGDB is not set ++CONFIG_CFG80211_WEXT=y ++CONFIG_WIRELESS_EXT_SYSFS=y ++# CONFIG_LIB80211 is not set ++# CONFIG_MAC80211 is not set ++# CONFIG_WIMAX is not set ++# CONFIG_RFKILL is not set ++# CONFIG_NET_9P is not set ++# CONFIG_CAIF is not set ++# CONFIG_CEPH_LIB is not set ++# CONFIG_NFC is not set ++ ++# ++# Device Drivers ++# ++ ++# ++# Generic Driver Options ++# ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++# CONFIG_DEVTMPFS is not set ++CONFIG_STANDALONE=y ++CONFIG_PREVENT_FIRMWARE_BUILD=y ++CONFIG_FW_LOADER=y ++CONFIG_FIRMWARE_IN_KERNEL=y ++CONFIG_EXTRA_FIRMWARE="" ++# CONFIG_DEBUG_DRIVER is not set ++# CONFIG_DEBUG_DEVRES is not set ++# CONFIG_SYS_HYPERVISOR is not set ++# CONFIG_CONNECTOR is not set ++# CONFIG_MTD is not set ++# CONFIG_PARPORT is not set ++CONFIG_BLK_DEV=y ++# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 ++# CONFIG_BLK_DEV_CRYPTOLOOP is not set ++ ++# ++# DRBD disabled because PROC_FS, INET or CONNECTOR not selected ++# ++# CONFIG_BLK_DEV_NBD is not set ++# CONFIG_BLK_DEV_UB is not set ++CONFIG_BLK_DEV_RAM=y ++CONFIG_BLK_DEV_RAM_COUNT=16 ++CONFIG_BLK_DEV_RAM_SIZE=4096 ++# CONFIG_BLK_DEV_XIP is not set ++# CONFIG_CDROM_PKTCDVD is not set ++# CONFIG_ATA_OVER_ETH is not set ++# CONFIG_MG_DISK is not set ++# CONFIG_BLK_DEV_RBD is not set ++# CONFIG_SENSORS_LIS3LV02D is not set ++CONFIG_MISC_DEVICES=y ++# CONFIG_ENCLOSURE_SERVICES is not set ++# CONFIG_C2PORT is not set ++ ++# ++# EEPROM support ++# ++# CONFIG_EEPROM_93CX6 is not set ++# CONFIG_IWMC3200TOP is not set ++ ++# ++# Texas Instruments shared transport line discipline ++# ++# CONFIG_TI_ST is not set ++CONFIG_BCM2708_VCHIQ=y ++CONFIG_HAVE_IDE=y ++# CONFIG_IDE is not set ++ ++# ++# SCSI device support ++# ++CONFIG_SCSI_MOD=y ++# CONFIG_RAID_ATTRS is not set ++CONFIG_SCSI=y ++CONFIG_SCSI_DMA=y ++# CONFIG_SCSI_TGT is not set ++# CONFIG_SCSI_NETLINK is not set ++# CONFIG_SCSI_PROC_FS is not set ++ ++# ++# SCSI support type (disk, tape, CD-ROM) ++# ++CONFIG_BLK_DEV_SD=m ++# CONFIG_CHR_DEV_ST is not set ++# CONFIG_CHR_DEV_OSST is not set ++CONFIG_BLK_DEV_SR=m ++# CONFIG_BLK_DEV_SR_VENDOR is not set ++# CONFIG_CHR_DEV_SG is not set ++# CONFIG_CHR_DEV_SCH is not set ++CONFIG_SCSI_MULTI_LUN=y ++# CONFIG_SCSI_CONSTANTS is not set ++# CONFIG_SCSI_LOGGING is not set ++# CONFIG_SCSI_SCAN_ASYNC is not set ++CONFIG_SCSI_WAIT_SCAN=m ++ ++# ++# SCSI Transports ++# ++# CONFIG_SCSI_SPI_ATTRS is not set ++# CONFIG_SCSI_FC_ATTRS is not set ++# CONFIG_SCSI_ISCSI_ATTRS is not set ++# CONFIG_SCSI_SAS_ATTRS is not set ++# CONFIG_SCSI_SAS_LIBSAS is not set ++# CONFIG_SCSI_SRP_ATTRS is not set ++# CONFIG_SCSI_LOWLEVEL is not set ++# CONFIG_SCSI_DH is not set ++# CONFIG_SCSI_OSD_INITIATOR is not set ++# CONFIG_ATA is not set ++# CONFIG_MD is not set ++# CONFIG_TARGET_CORE is not set ++CONFIG_NETDEVICES=y ++# CONFIG_DUMMY is not set ++# CONFIG_BONDING is not set ++# CONFIG_MACVLAN is not set ++# CONFIG_EQUALIZER is not set ++CONFIG_TUN=m ++# CONFIG_VETH is not set ++CONFIG_MII=y ++CONFIG_PHYLIB=m ++ ++# ++# MII PHY device drivers ++# ++# CONFIG_MARVELL_PHY is not set ++# CONFIG_DAVICOM_PHY is not set ++# CONFIG_QSEMI_PHY is not set ++# CONFIG_LXT_PHY is not set ++# CONFIG_CICADA_PHY is not set ++# CONFIG_VITESSE_PHY is not set ++# CONFIG_SMSC_PHY is not set ++# CONFIG_BROADCOM_PHY is not set ++# CONFIG_ICPLUS_PHY is not set ++# CONFIG_REALTEK_PHY is not set ++# CONFIG_NATIONAL_PHY is not set ++# CONFIG_STE10XP is not set ++# CONFIG_LSI_ET1011C_PHY is not set ++# CONFIG_MICREL_PHY is not set ++CONFIG_MDIO_BITBANG=m ++# CONFIG_MDIO_GPIO is not set ++CONFIG_NET_ETHERNET=y ++CONFIG_AX88796=m ++# CONFIG_AX88796_93CX6 is not set ++# CONFIG_SMC91X is not set ++# CONFIG_DM9000 is not set ++# CONFIG_ETHOC is not set ++# CONFIG_SMC911X is not set ++# CONFIG_SMSC911X is not set ++# CONFIG_DNET is not set ++# CONFIG_IBM_NEW_EMAC_ZMII is not set ++# CONFIG_IBM_NEW_EMAC_RGMII is not set ++# CONFIG_IBM_NEW_EMAC_TAH is not set ++# CONFIG_IBM_NEW_EMAC_EMAC4 is not set ++# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set ++# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set ++# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set ++# CONFIG_B44 is not set ++# CONFIG_KS8851_MLL is not set ++# CONFIG_FTMAC100 is not set ++# CONFIG_NETDEV_1000 is not set ++# CONFIG_NETDEV_10000 is not set ++# CONFIG_WLAN is not set ++ ++# ++# Enable WiMAX (Networking options) to see the WiMAX drivers ++# ++ ++# ++# USB Network Adapters ++# ++# CONFIG_USB_CATC is not set ++# CONFIG_USB_KAWETH is not set ++# CONFIG_USB_PEGASUS is not set ++# CONFIG_USB_RTL8150 is not set ++CONFIG_USB_USBNET=y ++CONFIG_USB_NET_AX8817X=m ++# CONFIG_USB_NET_CDCETHER is not set ++# CONFIG_USB_NET_CDC_EEM is not set ++CONFIG_USB_NET_CDC_NCM=y ++# CONFIG_USB_NET_DM9601 is not set ++# CONFIG_USB_NET_SMSC75XX is not set ++CONFIG_USB_NET_SMSC95XX=y ++# CONFIG_USB_NET_GL620A is not set ++# CONFIG_USB_NET_NET1080 is not set ++# CONFIG_USB_NET_PLUSB is not set ++# CONFIG_USB_NET_MCS7830 is not set ++# CONFIG_USB_NET_RNDIS_HOST is not set ++# CONFIG_USB_NET_CDC_SUBSET is not set ++# CONFIG_USB_NET_ZAURUS is not set ++# CONFIG_USB_NET_CX82310_ETH is not set ++# CONFIG_USB_NET_KALMIA is not set ++# CONFIG_USB_NET_INT51X1 is not set ++# CONFIG_USB_IPHETH is not set ++# CONFIG_USB_SIERRA_NET is not set ++# CONFIG_WAN is not set ++ ++# ++# CAIF transport drivers ++# ++# CONFIG_PPP is not set ++# CONFIG_SLIP is not set ++# CONFIG_NETCONSOLE is not set ++# CONFIG_NETPOLL is not set ++# CONFIG_NET_POLL_CONTROLLER is not set ++# CONFIG_ISDN is not set ++# CONFIG_PHONE is not set ++ ++# ++# Input device support ++# ++CONFIG_INPUT=y ++# CONFIG_INPUT_FF_MEMLESS is not set ++# CONFIG_INPUT_POLLDEV is not set ++# CONFIG_INPUT_SPARSEKMAP is not set ++ ++# ++# Userland interfaces ++# ++CONFIG_INPUT_MOUSEDEV=y ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 ++CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 ++# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_EVDEV=m ++# CONFIG_INPUT_EVBUG is not set ++ ++# ++# Input Device Drivers ++# ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++# CONFIG_INPUT_JOYSTICK is not set ++# CONFIG_INPUT_TABLET is not set ++# CONFIG_INPUT_TOUCHSCREEN is not set ++CONFIG_INPUT_MISC=y ++# CONFIG_INPUT_AD714X is not set ++# CONFIG_INPUT_ATI_REMOTE is not set ++# CONFIG_INPUT_ATI_REMOTE2 is not set ++# CONFIG_INPUT_KEYSPAN_REMOTE is not set ++# CONFIG_INPUT_POWERMATE is not set ++# CONFIG_INPUT_YEALINK is not set ++# CONFIG_INPUT_CM109 is not set ++CONFIG_INPUT_UINPUT=m ++# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set ++# CONFIG_INPUT_ADXL34X is not set ++# CONFIG_INPUT_CMA3000 is not set ++ ++# ++# Hardware I/O ports ++# ++CONFIG_SERIO=m ++CONFIG_SERIO_SERPORT=m ++# CONFIG_SERIO_AMBAKMI is not set ++# CONFIG_SERIO_LIBPS2 is not set ++CONFIG_SERIO_RAW=m ++# CONFIG_SERIO_ALTERA_PS2 is not set ++# CONFIG_SERIO_PS2MULT is not set ++CONFIG_GAMEPORT=m ++CONFIG_GAMEPORT_NS558=m ++CONFIG_GAMEPORT_L4=m ++ ++# ++# Character devices ++# ++CONFIG_VT=y ++CONFIG_CONSOLE_TRANSLATIONS=y ++CONFIG_VT_CONSOLE=y ++CONFIG_HW_CONSOLE=y ++CONFIG_VT_HW_CONSOLE_BINDING=y ++CONFIG_UNIX98_PTYS=y ++# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_SERIAL_NONSTANDARD is not set ++# CONFIG_N_GSM is not set ++# CONFIG_TRACE_SINK is not set ++# CONFIG_DEVKMEM is not set ++ ++# ++# Serial drivers ++# ++# CONFIG_SERIAL_8250 is not set ++ ++# ++# Non-8250 serial port support ++# ++# CONFIG_SERIAL_AMBA_PL010 is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++CONFIG_SERIAL_CORE=y ++CONFIG_SERIAL_CORE_CONSOLE=y ++# CONFIG_SERIAL_TIMBERDALE is not set ++# CONFIG_SERIAL_ALTERA_JTAGUART is not set ++# CONFIG_SERIAL_ALTERA_UART is not set ++# CONFIG_SERIAL_XILINX_PS_UART is not set ++# CONFIG_TTY_PRINTK is not set ++# CONFIG_HVC_DCC is not set ++# CONFIG_IPMI_HANDLER is not set ++# CONFIG_HW_RANDOM is not set ++# CONFIG_R3964 is not set ++CONFIG_RAW_DRIVER=y ++CONFIG_MAX_RAW_DEVS=256 ++# CONFIG_TCG_TPM is not set ++# CONFIG_RAMOOPS is not set ++# CONFIG_I2C is not set ++# CONFIG_SPI is not set ++ ++# ++# PPS support ++# ++# CONFIG_PPS is not set ++ ++# ++# PPS generators support ++# ++ ++# ++# PTP clock support ++# ++ ++# ++# Enable Device Drivers -> PPS to see the PTP clock options. ++# ++CONFIG_ARCH_REQUIRE_GPIOLIB=y ++CONFIG_GPIOLIB=y ++# CONFIG_DEBUG_GPIO is not set ++CONFIG_GPIO_SYSFS=y ++ ++# ++# Memory mapped GPIO drivers: ++# ++# CONFIG_GPIO_GENERIC_PLATFORM is not set ++# CONFIG_GPIO_IT8761E is not set ++# CONFIG_GPIO_PL061 is not set ++ ++# ++# I2C GPIO expanders: ++# ++ ++# ++# PCI GPIO expanders: ++# ++ ++# ++# SPI GPIO expanders: ++# ++ ++# ++# AC97 GPIO expanders: ++# ++ ++# ++# MODULbus GPIO expanders: ++# ++# CONFIG_W1 is not set ++# CONFIG_POWER_SUPPLY is not set ++# CONFIG_HWMON is not set ++# CONFIG_THERMAL is not set ++# CONFIG_WATCHDOG is not set ++CONFIG_SSB_POSSIBLE=y ++ ++# ++# Sonics Silicon Backplane ++# ++# CONFIG_SSB is not set ++CONFIG_BCMA_POSSIBLE=y ++ ++# ++# Broadcom specific AMBA ++# ++# CONFIG_BCMA is not set ++# CONFIG_MFD_SUPPORT is not set ++# CONFIG_REGULATOR is not set ++# CONFIG_MEDIA_SUPPORT is not set ++ ++# ++# Graphics support ++# ++# CONFIG_DRM is not set ++# CONFIG_VGASTATE is not set ++# CONFIG_VIDEO_OUTPUT_CONTROL is not set ++CONFIG_FB=y ++# CONFIG_FIRMWARE_EDID is not set ++# CONFIG_FB_DDC is not set ++# CONFIG_FB_BOOT_VESA_SUPPORT is not set ++CONFIG_FB_CFB_FILLRECT=y ++CONFIG_FB_CFB_COPYAREA=y ++CONFIG_FB_CFB_IMAGEBLIT=y ++# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set ++# CONFIG_FB_SYS_FILLRECT is not set ++# CONFIG_FB_SYS_COPYAREA is not set ++# CONFIG_FB_SYS_IMAGEBLIT is not set ++# CONFIG_FB_FOREIGN_ENDIAN is not set ++# CONFIG_FB_SYS_FOPS is not set ++# CONFIG_FB_WMT_GE_ROPS is not set ++# CONFIG_FB_SVGALIB is not set ++# CONFIG_FB_MACMODES is not set ++# CONFIG_FB_BACKLIGHT is not set ++# CONFIG_FB_MODE_HELPERS is not set ++# CONFIG_FB_TILEBLITTING is not set ++ ++# ++# Frame buffer hardware drivers ++# ++CONFIG_FB_BCM2708=y ++# CONFIG_FB_ARMCLCD is not set ++# CONFIG_FB_S1D13XXX is not set ++# CONFIG_FB_UDL is not set ++# CONFIG_FB_VIRTUAL is not set ++# CONFIG_FB_METRONOME is not set ++# CONFIG_FB_BROADSHEET is not set ++# CONFIG_BACKLIGHT_LCD_SUPPORT is not set ++ ++# ++# Display device support ++# ++# CONFIG_DISPLAY_SUPPORT is not set ++ ++# ++# Console display driver support ++# ++CONFIG_DUMMY_CONSOLE=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set ++# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set ++# CONFIG_FONTS is not set ++CONFIG_FONT_8x8=y ++CONFIG_FONT_8x16=y ++CONFIG_LOGO=y ++# CONFIG_LOGO_LINUX_MONO is not set ++# CONFIG_LOGO_LINUX_VGA16 is not set ++CONFIG_LOGO_LINUX_CLUT224=y ++# CONFIG_SOUND is not set ++CONFIG_HID_SUPPORT=y ++CONFIG_HID=y ++# CONFIG_HIDRAW is not set ++ ++# ++# USB Input Devices ++# ++CONFIG_USB_HID=y ++CONFIG_HID_PID=y ++CONFIG_USB_HIDDEV=y ++ ++# ++# Special HID drivers ++# ++CONFIG_HID_A4TECH=m ++# CONFIG_HID_ACRUX is not set ++CONFIG_HID_APPLE=m ++CONFIG_HID_BELKIN=m ++CONFIG_HID_CHERRY=m ++CONFIG_HID_CHICONY=m ++CONFIG_HID_CYPRESS=m ++CONFIG_HID_DRAGONRISE=m ++# CONFIG_DRAGONRISE_FF is not set ++# CONFIG_HID_EMS_FF is not set ++CONFIG_HID_EZKEY=m ++# CONFIG_HID_HOLTEK is not set ++# CONFIG_HID_KEYTOUCH is not set ++CONFIG_HID_KYE=m ++# CONFIG_HID_UCLOGIC is not set ++# CONFIG_HID_WALTOP is not set ++CONFIG_HID_GYRATION=m ++CONFIG_HID_TWINHAN=m ++CONFIG_HID_KENSINGTON=m ++# CONFIG_HID_LCPOWER is not set ++CONFIG_HID_LOGITECH=m ++# CONFIG_LOGITECH_FF is not set ++# CONFIG_LOGIRUMBLEPAD2_FF is not set ++# CONFIG_LOGIG940_FF is not set ++# CONFIG_LOGIWII_FF is not set ++CONFIG_HID_MICROSOFT=m ++CONFIG_HID_MONTEREY=m ++# CONFIG_HID_MULTITOUCH is not set ++# CONFIG_HID_NTRIG is not set ++CONFIG_HID_ORTEK=m ++CONFIG_HID_PANTHERLORD=m ++# CONFIG_PANTHERLORD_FF is not set ++CONFIG_HID_PETALYNX=m ++# CONFIG_HID_PICOLCD is not set ++# CONFIG_HID_QUANTA is not set ++# CONFIG_HID_ROCCAT is not set ++CONFIG_HID_SAMSUNG=m ++CONFIG_HID_SONY=m ++# CONFIG_HID_SPEEDLINK is not set ++CONFIG_HID_SUNPLUS=m ++CONFIG_HID_GREENASIA=m ++# CONFIG_GREENASIA_FF is not set ++CONFIG_HID_SMARTJOYPLUS=m ++# CONFIG_SMARTJOYPLUS_FF is not set ++CONFIG_HID_TOPSEED=m ++CONFIG_HID_THRUSTMASTER=m ++# CONFIG_THRUSTMASTER_FF is not set ++CONFIG_HID_ZEROPLUS=m ++# CONFIG_ZEROPLUS_FF is not set ++# CONFIG_HID_ZYDACRON is not set ++CONFIG_USB_SUPPORT=y ++CONFIG_USB_ARCH_HAS_HCD=y ++# CONFIG_USB_ARCH_HAS_OHCI is not set ++# CONFIG_USB_ARCH_HAS_EHCI is not set ++CONFIG_USB=y ++# CONFIG_USB_DEBUG is not set ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++ ++# ++# Miscellaneous USB options ++# ++# CONFIG_USB_DEVICEFS is not set ++CONFIG_USB_DEVICE_CLASS=y ++# CONFIG_USB_DYNAMIC_MINORS is not set ++# CONFIG_USB_OTG_WHITELIST is not set ++# CONFIG_USB_OTG_BLACKLIST_HUB is not set ++CONFIG_USB_MON=m ++# CONFIG_USB_WUSB is not set ++# CONFIG_USB_WUSB_CBAF is not set ++ ++# ++# USB Host Controller Drivers ++# ++# CONFIG_USB_C67X00_HCD is not set ++# CONFIG_USB_OXU210HP_HCD is not set ++# CONFIG_USB_ISP116X_HCD is not set ++# CONFIG_USB_ISP1760_HCD is not set ++# CONFIG_USB_ISP1362_HCD is not set ++# CONFIG_USB_SL811_HCD is not set ++# CONFIG_USB_R8A66597_HCD is not set ++# CONFIG_USB_HWA_HCD is not set ++CONFIG_USB_DWCOTG=y ++ ++# ++# USB Device Class drivers ++# ++# CONFIG_USB_ACM is not set ++# CONFIG_USB_PRINTER is not set ++# CONFIG_USB_WDM is not set ++# CONFIG_USB_TMC is not set ++ ++# ++# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may ++# ++ ++# ++# also be needed; see USB_STORAGE Help for more info ++# ++CONFIG_USB_STORAGE=y ++# CONFIG_USB_STORAGE_DEBUG is not set ++# CONFIG_USB_STORAGE_REALTEK is not set ++# CONFIG_USB_STORAGE_DATAFAB is not set ++# CONFIG_USB_STORAGE_FREECOM is not set ++# CONFIG_USB_STORAGE_ISD200 is not set ++# CONFIG_USB_STORAGE_USBAT is not set ++# CONFIG_USB_STORAGE_SDDR09 is not set ++# CONFIG_USB_STORAGE_SDDR55 is not set ++# CONFIG_USB_STORAGE_JUMPSHOT is not set ++# CONFIG_USB_STORAGE_ALAUDA is not set ++# CONFIG_USB_STORAGE_ONETOUCH is not set ++# CONFIG_USB_STORAGE_KARMA is not set ++# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set ++# CONFIG_USB_STORAGE_ENE_UB6250 is not set ++# CONFIG_USB_UAS is not set ++CONFIG_USB_LIBUSUAL=y ++ ++# ++# USB Imaging devices ++# ++# CONFIG_USB_MDC800 is not set ++# CONFIG_USB_MICROTEK is not set ++ ++# ++# USB port drivers ++# ++# CONFIG_USB_SERIAL is not set ++ ++# ++# USB Miscellaneous drivers ++# ++# CONFIG_USB_EMI62 is not set ++# CONFIG_USB_EMI26 is not set ++# CONFIG_USB_ADUTUX is not set ++# CONFIG_USB_SEVSEG is not set ++# CONFIG_USB_RIO500 is not set ++# CONFIG_USB_LEGOTOWER is not set ++# CONFIG_USB_LCD is not set ++# CONFIG_USB_LED is not set ++# CONFIG_USB_CYPRESS_CY7C63 is not set ++# CONFIG_USB_CYTHERM is not set ++# CONFIG_USB_IDMOUSE is not set ++# CONFIG_USB_FTDI_ELAN is not set ++# CONFIG_USB_APPLEDISPLAY is not set ++# CONFIG_USB_LD is not set ++# CONFIG_USB_TRANCEVIBRATOR is not set ++# CONFIG_USB_IOWARRIOR is not set ++# CONFIG_USB_TEST is not set ++# CONFIG_USB_ISIGHTFW is not set ++# CONFIG_USB_YUREX is not set ++# CONFIG_USB_GADGET is not set ++ ++# ++# OTG and related infrastructure ++# ++# CONFIG_USB_GPIO_VBUS is not set ++# CONFIG_USB_ULPI is not set ++# CONFIG_NOP_USB_XCEIV is not set ++CONFIG_MMC=y ++# CONFIG_MMC_DEBUG is not set ++# CONFIG_MMC_UNSAFE_RESUME is not set ++# CONFIG_MMC_CLKGATE is not set ++ ++# ++# MMC/SD/SDIO Card Drivers ++# ++CONFIG_MMC_BLOCK=y ++CONFIG_MMC_BLOCK_MINORS=8 ++CONFIG_MMC_BLOCK_BOUNCE=y ++# CONFIG_SDIO_UART is not set ++# CONFIG_MMC_TEST is not set ++ ++# ++# MMC/SD/SDIO Host Controller Drivers ++# ++# CONFIG_MMC_ARMMMCI is not set ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_IO_ACCESSORS=y ++CONFIG_MMC_SDHCI_PLTFM=y ++# CONFIG_MMC_SDHCI_PXAV3 is not set ++# CONFIG_MMC_SDHCI_PXAV2 is not set ++CONFIG_MMC_SDHCI_BCM2708=y ++CONFIG_MMC_SDHCI_BCM2708_DMA=y ++# CONFIG_MMC_BCM2708 is not set ++# CONFIG_MMC_DW is not set ++# CONFIG_MMC_VUB300 is not set ++# CONFIG_MMC_USHC is not set ++# CONFIG_MEMSTICK is not set ++CONFIG_NEW_LEDS=y ++CONFIG_LEDS_CLASS=y ++ ++# ++# LED drivers ++# ++CONFIG_LEDS_GPIO=y ++# CONFIG_LEDS_LT3593 is not set ++CONFIG_LEDS_TRIGGERS=y ++ ++# ++# LED Triggers ++# ++CONFIG_LEDS_TRIGGER_TIMER=m ++CONFIG_LEDS_TRIGGER_HEARTBEAT=m ++# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set ++# CONFIG_LEDS_TRIGGER_GPIO is not set ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=m ++ ++# ++# iptables trigger is under Netfilter config (LED target) ++# ++# CONFIG_ACCESSIBILITY is not set ++CONFIG_RTC_LIB=y ++# CONFIG_RTC_CLASS is not set ++# CONFIG_DMADEVICES is not set ++# CONFIG_AUXDISPLAY is not set ++# CONFIG_UIO is not set ++ ++# ++# Virtio drivers ++# ++# CONFIG_VIRTIO_BALLOON is not set ++# CONFIG_STAGING is not set ++CONFIG_CLKDEV_LOOKUP=y ++# CONFIG_IOMMU_SUPPORT is not set ++# CONFIG_VIRT_DRIVERS is not set ++ ++# ++# File systems ++# ++CONFIG_EXT2_FS=m ++CONFIG_EXT2_FS_XATTR=y ++CONFIG_EXT2_FS_POSIX_ACL=y ++CONFIG_EXT2_FS_SECURITY=y ++CONFIG_EXT2_FS_XIP=y ++CONFIG_EXT3_FS=y ++# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set ++CONFIG_EXT3_FS_XATTR=y ++CONFIG_EXT3_FS_POSIX_ACL=y ++CONFIG_EXT3_FS_SECURITY=y ++CONFIG_EXT4_FS=m ++CONFIG_EXT4_FS_XATTR=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++# CONFIG_EXT4_DEBUG is not set ++CONFIG_FS_XIP=y ++CONFIG_JBD=y ++CONFIG_JBD2=m ++CONFIG_FS_MBCACHE=y ++# CONFIG_REISERFS_FS is not set ++# CONFIG_JFS_FS is not set ++# CONFIG_XFS_FS is not set ++# CONFIG_GFS2_FS is not set ++# CONFIG_OCFS2_FS is not set ++# CONFIG_BTRFS_FS is not set ++# CONFIG_NILFS2_FS is not set ++CONFIG_FS_POSIX_ACL=y ++CONFIG_FILE_LOCKING=y ++CONFIG_FSNOTIFY=y ++CONFIG_DNOTIFY=y ++CONFIG_INOTIFY_USER=y ++# CONFIG_FANOTIFY is not set ++# CONFIG_QUOTA is not set ++# CONFIG_QUOTACTL is not set ++CONFIG_AUTOFS4_FS=y ++CONFIG_FUSE_FS=m ++CONFIG_CUSE=m ++ ++# ++# Caches ++# ++CONFIG_FSCACHE=y ++# CONFIG_FSCACHE_STATS is not set ++# CONFIG_FSCACHE_HISTOGRAM is not set ++# CONFIG_FSCACHE_DEBUG is not set ++# CONFIG_FSCACHE_OBJECT_LIST is not set ++CONFIG_CACHEFILES=y ++# CONFIG_CACHEFILES_DEBUG is not set ++# CONFIG_CACHEFILES_HISTOGRAM is not set ++ ++# ++# CD-ROM/DVD Filesystems ++# ++CONFIG_ISO9660_FS=m ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_UDF_FS=m ++CONFIG_UDF_NLS=y ++ ++# ++# DOS/FAT/NT Filesystems ++# ++CONFIG_FAT_FS=y ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_CODEPAGE=437 ++CONFIG_FAT_DEFAULT_IOCHARSET="ascii" ++CONFIG_NTFS_FS=m ++# CONFIG_NTFS_DEBUG is not set ++# CONFIG_NTFS_RW is not set ++ ++# ++# Pseudo filesystems ++# ++CONFIG_PROC_FS=y ++CONFIG_PROC_SYSCTL=y ++CONFIG_PROC_PAGE_MONITOR=y ++CONFIG_SYSFS=y ++CONFIG_TMPFS=y ++# CONFIG_TMPFS_POSIX_ACL is not set ++# CONFIG_TMPFS_XATTR is not set ++# CONFIG_HUGETLB_PAGE is not set ++CONFIG_CONFIGFS_FS=y ++CONFIG_MISC_FILESYSTEMS=y ++# CONFIG_ADFS_FS is not set ++# CONFIG_AFFS_FS is not set ++# CONFIG_ECRYPT_FS is not set ++# CONFIG_HFS_FS is not set ++# CONFIG_HFSPLUS_FS is not set ++# CONFIG_BEFS_FS is not set ++# CONFIG_BFS_FS is not set ++# CONFIG_EFS_FS is not set ++# CONFIG_LOGFS is not set ++# CONFIG_CRAMFS is not set ++# CONFIG_SQUASHFS is not set ++# CONFIG_VXFS_FS is not set ++# CONFIG_MINIX_FS is not set ++# CONFIG_OMFS_FS is not set ++# CONFIG_HPFS_FS is not set ++# CONFIG_QNX4FS_FS is not set ++# CONFIG_ROMFS_FS is not set ++# CONFIG_PSTORE is not set ++# CONFIG_SYSV_FS is not set ++# CONFIG_UFS_FS is not set ++CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++# CONFIG_NFS_V4_1 is not set ++CONFIG_ROOT_NFS=y ++CONFIG_NFS_FSCACHE=y ++# CONFIG_NFS_USE_LEGACY_DNS is not set ++CONFIG_NFS_USE_KERNEL_DNS=y ++# CONFIG_NFS_USE_NEW_IDMAPPER is not set ++# CONFIG_NFSD is not set ++CONFIG_LOCKD=y ++CONFIG_LOCKD_V4=y ++CONFIG_NFS_ACL_SUPPORT=y ++CONFIG_NFS_COMMON=y ++CONFIG_SUNRPC=y ++CONFIG_SUNRPC_GSS=y ++# CONFIG_CEPH_FS is not set ++CONFIG_CIFS=m ++# CONFIG_CIFS_STATS is not set ++CONFIG_CIFS_WEAK_PW_HASH=y ++# CONFIG_CIFS_UPCALL is not set ++CONFIG_CIFS_XATTR=y ++CONFIG_CIFS_POSIX=y ++# CONFIG_CIFS_DEBUG2 is not set ++# CONFIG_CIFS_DFS_UPCALL is not set ++# CONFIG_CIFS_FSCACHE is not set ++# CONFIG_CIFS_ACL is not set ++# CONFIG_NCP_FS is not set ++# CONFIG_CODA_FS is not set ++# CONFIG_AFS_FS is not set ++ ++# ++# Partition Types ++# ++CONFIG_PARTITION_ADVANCED=y ++# CONFIG_ACORN_PARTITION is not set ++# CONFIG_OSF_PARTITION is not set ++# CONFIG_AMIGA_PARTITION is not set ++# CONFIG_ATARI_PARTITION is not set ++CONFIG_MAC_PARTITION=y ++CONFIG_MSDOS_PARTITION=y ++# CONFIG_BSD_DISKLABEL is not set ++# CONFIG_MINIX_SUBPARTITION is not set ++# CONFIG_SOLARIS_X86_PARTITION is not set ++# CONFIG_UNIXWARE_DISKLABEL is not set ++# CONFIG_LDM_PARTITION is not set ++# CONFIG_SGI_PARTITION is not set ++# CONFIG_ULTRIX_PARTITION is not set ++# CONFIG_SUN_PARTITION is not set ++# CONFIG_KARMA_PARTITION is not set ++CONFIG_EFI_PARTITION=y ++# CONFIG_SYSV68_PARTITION is not set ++CONFIG_NLS=y ++CONFIG_NLS_DEFAULT="utf8" ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_CODEPAGE_737=m ++CONFIG_NLS_CODEPAGE_775=m ++CONFIG_NLS_CODEPAGE_850=m ++CONFIG_NLS_CODEPAGE_852=m ++CONFIG_NLS_CODEPAGE_855=m ++CONFIG_NLS_CODEPAGE_857=m ++CONFIG_NLS_CODEPAGE_860=m ++CONFIG_NLS_CODEPAGE_861=m ++CONFIG_NLS_CODEPAGE_862=m ++CONFIG_NLS_CODEPAGE_863=m ++CONFIG_NLS_CODEPAGE_864=m ++CONFIG_NLS_CODEPAGE_865=m ++CONFIG_NLS_CODEPAGE_866=m ++CONFIG_NLS_CODEPAGE_869=m ++CONFIG_NLS_CODEPAGE_936=m ++CONFIG_NLS_CODEPAGE_950=m ++CONFIG_NLS_CODEPAGE_932=m ++CONFIG_NLS_CODEPAGE_949=m ++CONFIG_NLS_CODEPAGE_874=m ++CONFIG_NLS_ISO8859_8=m ++CONFIG_NLS_CODEPAGE_1250=m ++CONFIG_NLS_CODEPAGE_1251=m ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=m ++CONFIG_NLS_ISO8859_2=m ++CONFIG_NLS_ISO8859_3=m ++CONFIG_NLS_ISO8859_4=m ++CONFIG_NLS_ISO8859_5=m ++CONFIG_NLS_ISO8859_6=m ++CONFIG_NLS_ISO8859_7=m ++CONFIG_NLS_ISO8859_9=m ++CONFIG_NLS_ISO8859_13=m ++CONFIG_NLS_ISO8859_14=m ++CONFIG_NLS_ISO8859_15=m ++CONFIG_NLS_KOI8_R=m ++CONFIG_NLS_KOI8_U=m ++CONFIG_NLS_UTF8=m ++# CONFIG_DLM is not set ++ ++# ++# Kernel hacking ++# ++# CONFIG_PRINTK_TIME is not set ++CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 ++CONFIG_ENABLE_WARN_DEPRECATED=y ++CONFIG_ENABLE_MUST_CHECK=y ++CONFIG_FRAME_WARN=1024 ++# CONFIG_MAGIC_SYSRQ is not set ++# CONFIG_STRIP_ASM_SYMS is not set ++# CONFIG_UNUSED_SYMBOLS is not set ++# CONFIG_DEBUG_FS is not set ++# CONFIG_HEADERS_CHECK is not set ++# CONFIG_DEBUG_SECTION_MISMATCH is not set ++CONFIG_DEBUG_KERNEL=y ++# CONFIG_DEBUG_SHIRQ is not set ++# CONFIG_LOCKUP_DETECTOR is not set ++# CONFIG_HARDLOCKUP_DETECTOR is not set ++# CONFIG_DETECT_HUNG_TASK is not set ++# CONFIG_SCHED_DEBUG is not set ++# CONFIG_SCHEDSTATS is not set ++# CONFIG_TIMER_STATS is not set ++# CONFIG_DEBUG_OBJECTS is not set ++# CONFIG_DEBUG_SLAB is not set ++# CONFIG_DEBUG_KMEMLEAK is not set ++# CONFIG_DEBUG_RT_MUTEXES is not set ++# CONFIG_RT_MUTEX_TESTER is not set ++# CONFIG_DEBUG_SPINLOCK is not set ++# CONFIG_DEBUG_MUTEXES is not set ++# CONFIG_DEBUG_LOCK_ALLOC is not set ++# CONFIG_PROVE_LOCKING is not set ++# CONFIG_SPARSE_RCU_POINTER is not set ++# CONFIG_LOCK_STAT is not set ++# CONFIG_DEBUG_ATOMIC_SLEEP is not set ++# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set ++# CONFIG_DEBUG_STACK_USAGE is not set ++# CONFIG_DEBUG_KOBJECT is not set ++# CONFIG_DEBUG_BUGVERBOSE is not set ++# CONFIG_DEBUG_INFO is not set ++# CONFIG_DEBUG_VM is not set ++# CONFIG_DEBUG_WRITECOUNT is not set ++# CONFIG_DEBUG_MEMORY_INIT is not set ++# CONFIG_DEBUG_LIST is not set ++# CONFIG_TEST_LIST_SORT is not set ++# CONFIG_DEBUG_SG is not set ++# CONFIG_DEBUG_NOTIFIERS is not set ++# CONFIG_DEBUG_CREDENTIALS is not set ++CONFIG_FRAME_POINTER=y ++# CONFIG_BOOT_PRINTK_DELAY is not set ++# CONFIG_RCU_TORTURE_TEST is not set ++# CONFIG_BACKTRACE_SELF_TEST is not set ++# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set ++# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set ++# CONFIG_FAULT_INJECTION is not set ++# CONFIG_LATENCYTOP is not set ++# CONFIG_SYSCTL_SYSCALL_CHECK is not set ++# CONFIG_DEBUG_PAGEALLOC is not set ++CONFIG_HAVE_FUNCTION_TRACER=y ++CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y ++CONFIG_HAVE_DYNAMIC_FTRACE=y ++CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y ++CONFIG_HAVE_C_RECORDMCOUNT=y ++CONFIG_TRACING_SUPPORT=y ++# CONFIG_FTRACE is not set ++# CONFIG_DMA_API_DEBUG is not set ++# CONFIG_ATOMIC64_SELFTEST is not set ++# CONFIG_SAMPLES is not set ++CONFIG_HAVE_ARCH_KGDB=y ++# CONFIG_KGDB is not set ++# CONFIG_TEST_KSTRTOX is not set ++# CONFIG_STRICT_DEVMEM is not set ++# CONFIG_ARM_UNWIND is not set ++# CONFIG_DEBUG_USER is not set ++# CONFIG_DEBUG_LL is not set ++# CONFIG_OC_ETM is not set ++ ++# ++# Security options ++# ++CONFIG_KEYS=y ++# CONFIG_KEYS_DEBUG_PROC_KEYS is not set ++# CONFIG_SECURITY_DMESG_RESTRICT is not set ++# CONFIG_SECURITY is not set ++# CONFIG_SECURITYFS is not set ++CONFIG_DEFAULT_SECURITY_DAC=y ++CONFIG_DEFAULT_SECURITY="" ++CONFIG_CRYPTO=y ++ ++# ++# Crypto core or helper ++# ++CONFIG_CRYPTO_ALGAPI=y ++CONFIG_CRYPTO_ALGAPI2=y ++CONFIG_CRYPTO_AEAD=m ++CONFIG_CRYPTO_AEAD2=y ++CONFIG_CRYPTO_BLKCIPHER=y ++CONFIG_CRYPTO_BLKCIPHER2=y ++CONFIG_CRYPTO_HASH=y ++CONFIG_CRYPTO_HASH2=y ++CONFIG_CRYPTO_RNG=m ++CONFIG_CRYPTO_RNG2=y ++CONFIG_CRYPTO_PCOMP2=y ++CONFIG_CRYPTO_MANAGER=y ++CONFIG_CRYPTO_MANAGER2=y ++CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y ++# CONFIG_CRYPTO_GF128MUL is not set ++# CONFIG_CRYPTO_NULL is not set ++CONFIG_CRYPTO_WORKQUEUE=y ++# CONFIG_CRYPTO_CRYPTD is not set ++CONFIG_CRYPTO_AUTHENC=m ++# CONFIG_CRYPTO_TEST is not set ++ ++# ++# Authenticated Encryption with Associated Data ++# ++# CONFIG_CRYPTO_CCM is not set ++# CONFIG_CRYPTO_GCM is not set ++CONFIG_CRYPTO_SEQIV=m ++ ++# ++# Block modes ++# ++CONFIG_CRYPTO_CBC=y ++# CONFIG_CRYPTO_CTR is not set ++# CONFIG_CRYPTO_CTS is not set ++CONFIG_CRYPTO_ECB=m ++# CONFIG_CRYPTO_LRW is not set ++# CONFIG_CRYPTO_PCBC is not set ++# CONFIG_CRYPTO_XTS is not set ++ ++# ++# Hash modes ++# ++CONFIG_CRYPTO_HMAC=y ++CONFIG_CRYPTO_XCBC=m ++# CONFIG_CRYPTO_VMAC is not set ++ ++# ++# Digest ++# ++CONFIG_CRYPTO_CRC32C=y ++# CONFIG_CRYPTO_GHASH is not set ++CONFIG_CRYPTO_MD4=m ++CONFIG_CRYPTO_MD5=y ++CONFIG_CRYPTO_MICHAEL_MIC=m ++# CONFIG_CRYPTO_RMD128 is not set ++# CONFIG_CRYPTO_RMD160 is not set ++# CONFIG_CRYPTO_RMD256 is not set ++# CONFIG_CRYPTO_RMD320 is not set ++CONFIG_CRYPTO_SHA1=y ++CONFIG_CRYPTO_SHA256=m ++CONFIG_CRYPTO_SHA512=m ++CONFIG_CRYPTO_TGR192=m ++CONFIG_CRYPTO_WP512=m ++ ++# ++# Ciphers ++# ++# CONFIG_CRYPTO_AES is not set ++# CONFIG_CRYPTO_ANUBIS is not set ++CONFIG_CRYPTO_ARC4=m ++# CONFIG_CRYPTO_BLOWFISH is not set ++# CONFIG_CRYPTO_CAMELLIA is not set ++CONFIG_CRYPTO_CAST5=m ++# CONFIG_CRYPTO_CAST6 is not set ++CONFIG_CRYPTO_DES=y ++# CONFIG_CRYPTO_FCRYPT is not set ++# CONFIG_CRYPTO_KHAZAD is not set ++# CONFIG_CRYPTO_SALSA20 is not set ++# CONFIG_CRYPTO_SEED is not set ++# CONFIG_CRYPTO_SERPENT is not set ++# CONFIG_CRYPTO_TEA is not set ++# CONFIG_CRYPTO_TWOFISH is not set ++ ++# ++# Compression ++# ++CONFIG_CRYPTO_DEFLATE=m ++# CONFIG_CRYPTO_ZLIB is not set ++# CONFIG_CRYPTO_LZO is not set ++ ++# ++# Random Number Generation ++# ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_USER_API_HASH is not set ++# CONFIG_CRYPTO_USER_API_SKCIPHER is not set ++# CONFIG_CRYPTO_HW is not set ++# CONFIG_BINARY_PRINTF is not set ++ ++# ++# Library routines ++# ++CONFIG_BITREVERSE=y ++CONFIG_CRC_CCITT=m ++CONFIG_CRC16=y ++# CONFIG_CRC_T10DIF is not set ++CONFIG_CRC_ITU_T=y ++CONFIG_CRC32=y ++# CONFIG_CRC7 is not set ++CONFIG_LIBCRC32C=y ++# CONFIG_CRC8 is not set ++CONFIG_ZLIB_INFLATE=m ++CONFIG_ZLIB_DEFLATE=m ++# CONFIG_XZ_DEC is not set ++# CONFIG_XZ_DEC_BCJ is not set ++CONFIG_HAS_IOMEM=y ++CONFIG_HAS_IOPORT=y ++CONFIG_HAS_DMA=y ++CONFIG_NLATTR=y ++CONFIG_GENERIC_ATOMIC64=y ++# CONFIG_AVERAGE is not set ++# CONFIG_CORDIC is not set +--- a/arch/arm/kernel/armksyms.c ++++ b/arch/arm/kernel/armksyms.c +@@ -46,6 +46,8 @@ extern void __aeabi_lmul(void); + extern void __aeabi_uidiv(void); + extern void __aeabi_uidivmod(void); + extern void __aeabi_ulcmp(void); ++extern void __aeabi_ldivmod(void); ++extern void __aeabi_uldivmod(void); + + extern void fpundefinstr(void); + +@@ -131,6 +133,8 @@ EXPORT_SYMBOL(__aeabi_lmul); + EXPORT_SYMBOL(__aeabi_uidiv); + EXPORT_SYMBOL(__aeabi_uidivmod); + EXPORT_SYMBOL(__aeabi_ulcmp); ++EXPORT_SYMBOL(__aeabi_ldivmod); ++EXPORT_SYMBOL(__aeabi_uldivmod); + #endif + + /* bitops */ +--- /dev/null ++++ b/arch/arm/lib/divdi3.c +@@ -0,0 +1,338 @@ ++/* 64-bit multiplication and division ++ Copyright (C) 1989, 1992-1999, 2000, 2001, 2002, 2003 ++ Free Software Foundation, Inc. ++ This file is part of the GNU C Library. ++ ++ The GNU C Library is free software; you can redistribute it and/or ++ modify it under the terms of the GNU Lesser General Public ++ License as published by the Free Software Foundation; either ++ version 2.1 of the License, or (at your option) any later version. ++ ++ The GNU C Library is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++ Lesser General Public License for more details. ++ ++ You should have received a copy of the GNU Lesser General Public ++ License along with the GNU C Library; if not, write to the Free ++ Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA ++ 02111-1307 USA. */ ++ ++#include "longlong.h" ++ ++#define W_TYPE_SIZE (sizeof(long)) ++ ++#ifdef __ARMEB__ ++struct DWstruct { long high, low;}; ++#else ++struct DWstruct { long low, high;}; ++#endif ++ ++typedef union { struct DWstruct s; long long ll; } DWunion; ++ ++/* Prototypes of exported functions. */ ++long long __divdi3 (long long u, long long v); ++long long __moddi3 (long long u, long long v); ++unsigned long long __udivdi3 (unsigned long long u, unsigned long long v); ++unsigned long long __umoddi3 (unsigned long long u, unsigned long long v); ++ ++static unsigned long long ++__udivmoddi4 (unsigned long long n, unsigned long long d, unsigned long long *rp) ++{ ++ DWunion ww; ++ DWunion nn, dd; ++ DWunion rr; ++ unsigned long d0, d1, n0, n1, n2; ++ unsigned long q0, q1; ++ unsigned long b, bm; ++ ++ nn.ll = n; ++ dd.ll = d; ++ ++ d0 = dd.s.low; ++ d1 = dd.s.high; ++ n0 = nn.s.low; ++ n1 = nn.s.high; ++ ++#if !UDIV_NEEDS_NORMALIZATION ++ if (d1 == 0) ++ { ++ if (d0 > n1) ++ { ++ /* 0q = nn / 0D */ ++ ++ udiv_qrnnd (q0, n0, n1, n0, d0); ++ q1 = 0; ++ ++ /* Remainder in n0. */ ++ } ++ else ++ { ++ /* qq = NN / 0d */ ++ ++ if (d0 == 0) ++ d0 = 1 / d0; /* Divide intentionally by zero. */ ++ ++ udiv_qrnnd (q1, n1, 0, n1, d0); ++ udiv_qrnnd (q0, n0, n1, n0, d0); ++ ++ /* Remainder in n0. */ ++ } ++ ++ if (rp != 0) ++ { ++ rr.s.low = n0; ++ rr.s.high = 0; ++ *rp = rr.ll; ++ } ++ } ++ ++#else /* UDIV_NEEDS_NORMALIZATION */ ++ ++ if (d1 == 0) ++ { ++ if (d0 > n1) ++ { ++ /* 0q = nn / 0D */ ++ ++ count_leading_zeros (bm, d0); ++ ++ if (bm != 0) ++ { ++ /* Normalize, i.e. make the most significant bit of the ++ denominator set. */ ++ ++ d0 = d0 << bm; ++ n1 = (n1 << bm) | (n0 >> (W_TYPE_SIZE - bm)); ++ n0 = n0 << bm; ++ } ++ ++ udiv_qrnnd (q0, n0, n1, n0, d0); ++ q1 = 0; ++ ++ /* Remainder in n0 >> bm. */ ++ } ++ else ++ { ++ /* qq = NN / 0d */ ++ ++ if (d0 == 0) ++ d0 = 1 / d0; /* Divide intentionally by zero. */ ++ ++ count_leading_zeros (bm, d0); ++ ++ if (bm == 0) ++ { ++ /* From (n1 >= d0) /\ (the most significant bit of d0 is set), ++ conclude (the most significant bit of n1 is set) /\ (the ++ leading quotient digit q1 = 1). ++ ++ This special case is necessary, not an optimization. ++ (Shifts counts of W_TYPE_SIZE are undefined.) */ ++ ++ n1 -= d0; ++ q1 = 1; ++ } ++ else ++ { ++ /* Normalize. */ ++ ++ b = W_TYPE_SIZE - bm; ++ ++ d0 = d0 << bm; ++ n2 = n1 >> b; ++ n1 = (n1 << bm) | (n0 >> b); ++ n0 = n0 << bm; ++ ++ udiv_qrnnd (q1, n1, n2, n1, d0); ++ } ++ ++ /* n1 != d0... */ ++ ++ udiv_qrnnd (q0, n0, n1, n0, d0); ++ ++ /* Remainder in n0 >> bm. */ ++ } ++ ++ if (rp != 0) ++ { ++ rr.s.low = n0 >> bm; ++ rr.s.high = 0; ++ *rp = rr.ll; ++ } ++ } ++#endif /* UDIV_NEEDS_NORMALIZATION */ ++ ++ else ++ { ++ if (d1 > n1) ++ { ++ /* 00 = nn / DD */ ++ ++ q0 = 0; ++ q1 = 0; ++ ++ /* Remainder in n1n0. */ ++ if (rp != 0) ++ { ++ rr.s.low = n0; ++ rr.s.high = n1; ++ *rp = rr.ll; ++ } ++ } ++ else ++ { ++ /* 0q = NN / dd */ ++ ++ count_leading_zeros (bm, d1); ++ if (bm == 0) ++ { ++ /* From (n1 >= d1) /\ (the most significant bit of d1 is set), ++ conclude (the most significant bit of n1 is set) /\ (the ++ quotient digit q0 = 0 or 1). ++ ++ This special case is necessary, not an optimization. */ ++ ++ /* The condition on the next line takes advantage of that ++ n1 >= d1 (true due to program flow). */ ++ if (n1 > d1 || n0 >= d0) ++ { ++ q0 = 1; ++ sub_ddmmss (n1, n0, n1, n0, d1, d0); ++ } ++ else ++ q0 = 0; ++ ++ q1 = 0; ++ ++ if (rp != 0) ++ { ++ rr.s.low = n0; ++ rr.s.high = n1; ++ *rp = rr.ll; ++ } ++ } ++ else ++ { ++ unsigned long m1, m0; ++ /* Normalize. */ ++ ++ b = W_TYPE_SIZE - bm; ++ ++ d1 = (d1 << bm) | (d0 >> b); ++ d0 = d0 << bm; ++ n2 = n1 >> b; ++ n1 = (n1 << bm) | (n0 >> b); ++ n0 = n0 << bm; ++ ++ udiv_qrnnd (q0, n1, n2, n1, d1); ++ umul_ppmm (m1, m0, q0, d0); ++ ++ if (m1 > n1 || (m1 == n1 && m0 > n0)) ++ { ++ q0--; ++ sub_ddmmss (m1, m0, m1, m0, d1, d0); ++ } ++ ++ q1 = 0; ++ ++ /* Remainder in (n1n0 - m1m0) >> bm. */ ++ if (rp != 0) ++ { ++ sub_ddmmss (n1, n0, n1, n0, m1, m0); ++ rr.s.low = (n1 << b) | (n0 >> bm); ++ rr.s.high = n1 >> bm; ++ *rp = rr.ll; ++ } ++ } ++ } ++ } ++ ++ ww.s.low = q0; ++ ww.s.high = q1; ++ return ww.ll; ++} ++ ++long long ++__divdi3 (long long u, long long v) ++{ ++ long c = 0; ++ long long w; ++ ++ if (u < 0) ++ { ++ c = ~c; ++ u = -u; ++ } ++ if (v < 0) ++ { ++ c = ~c; ++ v = -v; ++ } ++ w = __udivmoddi4 (u, v, 0); ++ if (c) ++ w = -w; ++ return w; ++} ++ ++long long ++__moddi3 (long long u, long long v) ++{ ++ long c = 0; ++ long long w; ++ ++ if (u < 0) ++ { ++ c = ~c; ++ u = -u; ++ } ++ if (v < 0) ++ v = -v; ++ __udivmoddi4 (u, v, &w); ++ if (c) ++ w = -w; ++ return w; ++} ++ ++unsigned long long ++__udivdi3 (unsigned long long u, unsigned long long v) ++{ ++ return __udivmoddi4 (u, v, 0); ++} ++ ++unsigned long long ++__umoddi3 (unsigned long long u, unsigned long long v) ++{ ++ unsigned long long w; ++ ++ __udivmoddi4 (u, v, &w); ++ return w; ++} ++ ++long long ++__gnu_ldivmod_helper (long long a, ++ ++ long long b, ++ long long *remainder) ++{ ++ long long quotient; ++ ++ quotient = __divdi3 (a, b); ++ *remainder = a - b * quotient; ++ ++ return quotient; ++} ++ ++unsigned long long ++ ++__gnu_uldivmod_helper (unsigned long long a, ++ ++ unsigned long long b, ++ unsigned long long *remainder) ++{ ++ unsigned long long quotient; ++ ++ quotient = __udivdi3 (a, b); ++ *remainder = a - b * quotient; ++ return quotient; ++} +--- a/arch/arm/lib/lib1funcs.S ++++ b/arch/arm/lib/lib1funcs.S +@@ -349,6 +349,33 @@ UNWIND(.save {r0, r1, ip, lr} ) + UNWIND(.fnend) + ENDPROC(__aeabi_idivmod) + ++/* Added 64x64 bit division for use with OTG USB driver - multi-precision ++ * arithmetic for RSA encyrption. ++ */ ++ENTRY(__aeabi_ldivmod) ++ ++ sub sp, sp, #8 ++ stmfd sp!, {sp, lr} ++ bl __gnu_ldivmod_helper ++ ldr lr, [sp, #4] ++ add sp, sp, #8 ++ ldmfd sp!, {r2, r3} ++ mov pc, lr ++ ++ENDPROC(__aeabi_ldivmod) ++ ++ENTRY(__aeabi_uldivmod) ++ ++ sub sp, sp, #8 ++ stmfd sp!, {sp, lr} ++ bl __gnu_uldivmod_helper ++ ldr lr, [sp, #4] ++ add sp, sp, #8 ++ ldmfd sp!, {r2, r3} ++ mov pc, lr ++ ++ENDPROC(__aeabi_uldivmod) ++ + #endif + + Ldiv0: +--- /dev/null ++++ b/arch/arm/lib/longlong.h +@@ -0,0 +1,151 @@ ++/* longlong.h -- based on code from gcc-2.95.3 ++ ++ definitions for mixed size 32/64 bit arithmetic. ++ Copyright (C) 1991, 92, 94, 95, 96, 1997, 1998 Free Software Foundation, Inc. ++ ++ This definition file 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, or (at your option) any later version. ++ ++ This definition file is distributed in the hope that it will be ++ useful, but WITHOUT ANY WARRANTY; without even the implied ++ warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. ++ See the GNU General Public License for more details. ++ ++ 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., 59 Temple Place - Suite 330, ++ Boston, MA 02111-1307, USA. */ ++ ++/* Borrowed from GCC 2.95.3, I Molton 29/07/01 */ ++ ++#define USItype unsigned long ++#define SI_TYPE_SIZE sizeof(USItype) ++ ++#define __BITS4 (SI_TYPE_SIZE / 4) ++#define __ll_B (1L << (SI_TYPE_SIZE / 2)) ++#define __ll_lowpart(t) ((USItype) (t) % __ll_B) ++#define __ll_highpart(t) ((USItype) (t) / __ll_B) ++ ++/* Define auxiliary asm macros. ++ ++ 1) umul_ppmm(high_prod, low_prod, multipler, multiplicand) ++ multiplies two USItype integers MULTIPLER and MULTIPLICAND, ++ and generates a two-part USItype product in HIGH_PROD and ++ LOW_PROD. ++ ++ 2) __umulsidi3(a,b) multiplies two USItype integers A and B, ++ and returns a UDItype product. This is just a variant of umul_ppmm. ++ ++ 3) udiv_qrnnd(quotient, remainder, high_numerator, low_numerator, ++ denominator) divides a two-word unsigned integer, composed by the ++ integers HIGH_NUMERATOR and LOW_NUMERATOR, by DENOMINATOR and ++ places the quotient in QUOTIENT and the remainder in REMAINDER. ++ HIGH_NUMERATOR must be less than DENOMINATOR for correct operation. ++ If, in addition, the most significant bit of DENOMINATOR must be 1, ++ then the pre-processor symbol UDIV_NEEDS_NORMALIZATION is defined to 1. ++ ++ 4) sdiv_qrnnd(quotient, remainder, high_numerator, low_numerator, ++ denominator). Like udiv_qrnnd but the numbers are signed. The ++ quotient is rounded towards 0. ++ ++ 5) count_leading_zeros(count, x) counts the number of zero-bits from ++ the msb to the first non-zero bit. This is the number of steps X ++ needs to be shifted left to set the msb. Undefined for X == 0. ++ ++ 6) add_ssaaaa(high_sum, low_sum, high_addend_1, low_addend_1, ++ high_addend_2, low_addend_2) adds two two-word unsigned integers, ++ composed by HIGH_ADDEND_1 and LOW_ADDEND_1, and HIGH_ADDEND_2 and ++ LOW_ADDEND_2 respectively. The result is placed in HIGH_SUM and ++ LOW_SUM. Overflow (i.e. carry out) is not stored anywhere, and is ++ lost. ++ ++ 7) sub_ddmmss(high_difference, low_difference, high_minuend, ++ low_minuend, high_subtrahend, low_subtrahend) subtracts two ++ two-word unsigned integers, composed by HIGH_MINUEND_1 and ++ LOW_MINUEND_1, and HIGH_SUBTRAHEND_2 and LOW_SUBTRAHEND_2 ++ respectively. The result is placed in HIGH_DIFFERENCE and ++ LOW_DIFFERENCE. Overflow (i.e. carry out) is not stored anywhere, ++ and is lost. ++ ++ If any of these macros are left undefined for a particular CPU, ++ C macros are used. */ ++ ++#if defined (__arm__) ++#define add_ssaaaa(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("adds %1, %4, %5 \n\ ++ adc %0, %2, %3" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "%r" ((USItype) (ah)), \ ++ "rI" ((USItype) (bh)), \ ++ "%r" ((USItype) (al)), \ ++ "rI" ((USItype) (bl))) ++#define sub_ddmmss(sh, sl, ah, al, bh, bl) \ ++ __asm__ ("subs %1, %4, %5 \n\ ++ sbc %0, %2, %3" \ ++ : "=r" ((USItype) (sh)), \ ++ "=&r" ((USItype) (sl)) \ ++ : "r" ((USItype) (ah)), \ ++ "rI" ((USItype) (bh)), \ ++ "r" ((USItype) (al)), \ ++ "rI" ((USItype) (bl))) ++#define umul_ppmm(xh, xl, a, b) \ ++ __asm__ ("%@ Inlined umul_ppmm\n" \ ++ "umull %r1, %r0, %r2, %r3" \ ++ : "=&r" ((USItype)(xh)), \ ++ "=r" ((USItype)(xl)) \ ++ : "r" ((USItype)(a)), \ ++ "r" ((USItype)(b)) \ ++ : "r0", "r1") ++#define count_leading_zeros(count, x) \ ++ __asm__ ("clz %0, %1" : "=r"(count) : "r"(x)) ++#define UMUL_TIME 20 ++#define UDIV_TIME 100 ++#endif /* __arm__ */ ++ ++#define __umulsidi3(u, v) \ ++ ({DIunion __w; \ ++ umul_ppmm (__w.s.high, __w.s.low, u, v); \ ++ __w.ll; }) ++ ++#define __udiv_qrnnd_c(q, r, n1, n0, d) \ ++ do { \ ++ USItype __d1, __d0, __q1, __q0; \ ++ USItype __r1, __r0, __m; \ ++ __d1 = __ll_highpart (d); \ ++ __d0 = __ll_lowpart (d); \ ++ \ ++ __r1 = (n1) % __d1; \ ++ __q1 = (n1) / __d1; \ ++ __m = (USItype) __q1 * __d0; \ ++ __r1 = __r1 * __ll_B | __ll_highpart (n0); \ ++ if (__r1 < __m) \ ++ { \ ++ __q1--, __r1 += (d); \ ++ if (__r1 >= (d)) /* i.e. we didn't get carry when adding to __r1 */\ ++ if (__r1 < __m) \ ++ __q1--, __r1 += (d); \ ++ } \ ++ __r1 -= __m; \ ++ \ ++ __r0 = __r1 % __d1; \ ++ __q0 = __r1 / __d1; \ ++ __m = (USItype) __q0 * __d0; \ ++ __r0 = __r0 * __ll_B | __ll_lowpart (n0); \ ++ if (__r0 < __m) \ ++ { \ ++ __q0--, __r0 += (d); \ ++ if (__r0 >= (d)) \ ++ if (__r0 < __m) \ ++ __q0--, __r0 += (d); \ ++ } \ ++ __r0 -= __m; \ ++ \ ++ (q) = (USItype) __q1 * __ll_B | __q0; \ ++ (r) = __r0; \ ++ } while (0) ++ ++#define UDIV_NEEDS_NORMALIZATION 1 ++#define udiv_qrnnd __udiv_qrnnd_c +--- /dev/null ++++ b/arch/arm/mach-bcm2708/Kconfig +@@ -0,0 +1,25 @@ ++menu "Broadcom BCM2708 Implementations" ++ depends on ARCH_BCM2708 ++ ++config MACH_BCM2708 ++ bool "Broadcom BCM2708 Development Platform" ++ select CPU_V6 ++ help ++ Include support for the Broadcom(R) BCM2708 platform. ++ ++config BCM2708_GPIO ++ bool "BCM2708 gpio support" ++ depends on MACH_BCM2708 ++ select ARCH_REQUIRE_GPIOLIB ++ default y ++ help ++ Include support for the Broadcom(R) BCM2708 gpio. ++ ++config BCM2708_VCMEM ++ bool "Videocore Memory" ++ depends on MACH_BCM2708 ++ default y ++ help ++ Helper for videocore memory access and total size allocation. ++ ++endmenu +--- /dev/null ++++ b/arch/arm/mach-bcm2708/Makefile +@@ -0,0 +1,8 @@ ++# ++# Makefile for the linux kernel. ++# ++ ++obj-$(CONFIG_MACH_BCM2708) += clock.o bcm2708.o armctrl.o vcio.o power.o dma.o ++obj-$(CONFIG_BCM2708_GPIO) += bcm2708_gpio.o ++obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o ++ +--- /dev/null ++++ b/arch/arm/mach-bcm2708/Makefile.boot +@@ -0,0 +1,3 @@ ++ zreladdr-y := 0x00008000 ++params_phys-y := 0x00000100 ++initrd_phys-y := 0x00800000 +--- /dev/null ++++ b/arch/arm/mach-bcm2708/armctrl.c +@@ -0,0 +1,399 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/armctrl.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#include <linux/init.h> ++#include <linux/list.h> ++#include <linux/io.h> ++#include <linux/version.h> ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,39) ++#include <linux/syscore_ops.h> ++#else ++#include <linux/sysdev.h> ++#endif ++#include <linux/interrupt.h> ++ ++#include <asm/mach/irq.h> ++#include <mach/hardware.h> ++#include "armctrl.h" ++ ++/* For support of kernels >= 3.0 assume only one VIC for now*/ ++static unsigned int remap_irqs[(INTERRUPT_ARASANSDIO + 1) - INTERRUPT_JPEG] = { ++ INTERRUPT_VC_JPEG, ++ INTERRUPT_VC_USB, ++ INTERRUPT_VC_3D, ++ INTERRUPT_VC_DMA2, ++ INTERRUPT_VC_DMA3, ++ INTERRUPT_VC_I2C, ++ INTERRUPT_VC_SPI, ++ INTERRUPT_VC_I2SPCM, ++ INTERRUPT_VC_SDIO, ++ INTERRUPT_VC_UART, ++ INTERRUPT_VC_ARASANSDIO ++}; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38) ++static void armctrl_mask_irq(struct irq_data *d) ++#else ++static void armctrl_mask_irq(unsigned int irq) ++#endif ++{ ++ static const unsigned int disables[4] = { ++ IO_ADDRESS(ARM_IRQ_DIBL1), ++ IO_ADDRESS(ARM_IRQ_DIBL2), ++ IO_ADDRESS(ARM_IRQ_DIBL3), ++ 0 ++ }; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38) ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); ++#else ++ unsigned int data = (unsigned int)get_irq_chip_data(irq); ++#endif ++ writel(1 << (data & 0x1f), __io(disables[(data >> 5) & 0x3])); ++} ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38) ++static void armctrl_unmask_irq(struct irq_data *d) ++#else ++static void armctrl_unmask_irq(unsigned int irq) ++#endif ++{ ++ static const unsigned int enables[4] = { ++ IO_ADDRESS(ARM_IRQ_ENBL1), ++ IO_ADDRESS(ARM_IRQ_ENBL2), ++ IO_ADDRESS(ARM_IRQ_ENBL3), ++ 0 ++ }; ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38) ++ unsigned int data = (unsigned int)irq_get_chip_data(d->irq); ++#else ++ unsigned int data = (unsigned int)get_irq_chip_data(irq); ++#endif ++ writel(1 << (data & 0x1f), __io(enables[(data >> 5) & 0x3])); ++} ++ ++#if defined(CONFIG_PM) ++ ++/* for kernels 3.xx use the new syscore_ops apis but for older kernels use the sys dev class */ ++ ++/* Static defines ++ * struct armctrl_device - VIC PM device (< 3.xx) ++ * @sysdev: The system device which is registered. (< 3.xx) ++ * @irq: The IRQ number for the base of the VIC. ++ * @base: The register base for the VIC. ++ * @resume_sources: A bitmask of interrupts for resume. ++ * @resume_irqs: The IRQs enabled for resume. ++ * @int_select: Save for VIC_INT_SELECT. ++ * @int_enable: Save for VIC_INT_ENABLE. ++ * @soft_int: Save for VIC_INT_SOFT. ++ * @protect: Save for VIC_PROTECT. ++ */ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39) ++struct armctrl_device { ++ struct sys_device sysdev; ++#else ++ struct armctrl_info { ++#endif ++ void __iomem *base; ++ int irq; ++ u32 resume_sources; ++ u32 resume_irqs; ++ u32 int_select; ++ u32 int_enable; ++ u32 soft_int; ++ u32 protect; ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39) ++}; ++#else ++ } armctrl; ++#endif ++ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39) ++ ++static struct armctrl_device armctrl_devices[1]; ++ ++static inline struct armctrl_device *to_vic(struct sys_device *sys) ++{ ++ return container_of(sys, struct armctrl_device, sysdev); ++} ++ ++static int armctrl_id; ++ ++static int armctrl_class_resume(struct sys_device *dev) ++{ ++#if 0 // FIXME ++ struct armctrl_device *armctrl = to_vic(dev); ++ void __iomem *base = armctrl->base; ++ ++ printk(KERN_DEBUG "%s: resuming armctrl at %p\n", __func__, base); ++ ++ writel(armctrl->int_select, base + VIC_INT_SELECT); ++ writel(armctrl->protect, base + VIC_PROTECT); ++ ++ /* set the enabled ints and then clear the non-enabled */ ++ writel(armctrl->int_enable, base + VIC_INT_ENABLE); ++ writel(~armctrl->int_enable, base + VIC_INT_ENABLE_CLEAR); ++ ++ /* and the same for the soft-int register */ ++ ++ writel(armctrl->soft_int, base + VIC_INT_SOFT); ++ writel(~armctrl->soft_int, base + VIC_INT_SOFT_CLEAR); ++#endif ++ return 0; ++} ++ ++static int armctrl_class_suspend(struct sys_device *dev, pm_message_t state) ++{ ++#if 0 // FIXME ++ struct armctrl_device *armctrl = to_vic(dev); ++ void __iomem *base = armctrl->base; ++ ++ printk(KERN_DEBUG "%s: suspending armctrl at %p\n", __func__, base); ++ ++ armctrl->int_select = readl(base + VIC_INT_SELECT); ++ armctrl->int_enable = readl(base + VIC_INT_ENABLE); ++ armctrl->soft_int = readl(base + VIC_INT_SOFT); ++ armctrl->protect = readl(base + VIC_PROTECT); ++ ++ /* set the interrupts (if any) that are used for ++ * resuming the system */ ++ ++ writel(armctrl->resume_irqs, base + VIC_INT_ENABLE); ++ writel(~armctrl->resume_irqs, base + VIC_INT_ENABLE_CLEAR); ++#endif ++ return 0; ++} ++ ++struct sysdev_class armctrl_class = { ++ .name = "armctrl", ++ .suspend = armctrl_class_suspend, ++ .resume = armctrl_class_resume, ++}; ++ ++#endif // < 2.6.39 ++ ++static int armctrl_suspend(void) ++{ ++ return 0; ++} ++ ++static void armctrl_resume(void) ++{ ++ return; ++} ++ ++ ++/** ++ * armctrl_pm_register - Register a VIC for later power management control ++ * @base: The base address of the VIC. ++ * @irq: The base IRQ for the VIC. ++ * @resume_sources: bitmask of interrupts allowed for resume sources. ++ * ++ * For older kernels (< 3.xx) do - ++ * Register the VIC with the system device tree so that it can be notified ++ * of suspend and resume requests and ensure that the correct actions are ++ * taken to re-instate the settings on resume. ++ */ ++static void __init armctrl_pm_register(void __iomem * base, unsigned int irq, ++ u32 resume_sources) ++{ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39) ++ struct armctrl_device *v; ++ ++ if (armctrl_id >= ARRAY_SIZE(armctrl_devices)) ++ printk(KERN_ERR ++ "%s: too few VICs, increase CONFIG_ARM_VIC_NR\n", ++ __func__); ++ else { ++ v = &armctrl_devices[armctrl_id]; ++ v->base = base; ++ v->resume_sources = resume_sources; ++ v->irq = irq; ++ armctrl_id++; ++ } ++#else ++ armctrl.base = base; ++ armctrl.resume_sources = resume_sources; ++ armctrl.irq = irq; ++#endif ++} ++ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39) ++ ++/** ++ * armctrl_pm_init - initicall to register VIC pm ++ * ++ * This is called via late_initcall() to register ++ * the resources for the VICs due to the early ++ * nature of the VIC's registration. ++*/ ++static int __init armctrl_pm_init(void) ++{ ++ struct armctrl_device *dev = armctrl_devices; ++ int err; ++ int id; ++ ++ if (armctrl_id == 0) ++ return 0; ++ ++ err = sysdev_class_register(&armctrl_class); ++ if (err) { ++ printk(KERN_ERR "%s: cannot register class\n", __func__); ++ return err; ++ } ++ ++ for (id = 0; id < armctrl_id; id++, dev++) { ++ dev->sysdev.id = id; ++ dev->sysdev.cls = &armctrl_class; ++ ++ err = sysdev_register(&dev->sysdev); ++ if (err) { ++ printk(KERN_ERR "%s: failed to register device\n", ++ __func__); ++ return err; ++ } ++ } ++ ++ return 0; ++} ++ ++late_initcall(armctrl_pm_init); ++ ++#endif // VERSION check ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38) ++static int armctrl_set_wake(struct irq_data *d, unsigned int on) ++#else ++static int armctrl_set_wake(unsigned int irq, unsigned int on) ++#endif ++{ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39) ++ struct armctrl_device *armctrl = &armctrl_devices[0]; ++#endif ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38) ++ unsigned int off = d->irq & 31; ++#else ++ unsigned int off = irq & 31; ++#endif ++ u32 bit = 1 << off; ++ ++#if LINUX_VERSION_CODE <= KERNEL_VERSION(2,6,39) ++ if (!armctrl) ++ return -EINVAL; ++ ++ if (!(bit & armctrl->resume_sources)) ++ return -EINVAL; ++ ++ if (on) ++ armctrl->resume_irqs |= bit; ++ else ++ armctrl->resume_irqs &= ~bit; ++#else ++ if (!(bit & armctrl.resume_sources)) ++ return -EINVAL; ++ ++ if (on) ++ armctrl.resume_irqs |= bit; ++ else ++ armctrl.resume_irqs &= ~bit; ++#endif ++ ++ return 0; ++} ++ ++#else ++static inline void armctrl_pm_register(void __iomem *base, unsigned int irq, ++ u32 arg1) ++{ ++} ++#define armctrl_suspend NULL ++#define armctrl_resume NULL ++#define armctrl_set_wake NULL ++#endif /* CONFIG_PM */ ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,39) ++ ++static struct syscore_ops armctrl_syscore_ops = { ++ .suspend = armctrl_suspend, ++ .resume = armctrl_resume, ++}; ++ ++/** ++ * armctrl_syscore_init - initicall to register VIC pm functions ++ * ++ * This is called via late_initcall() to register ++ * the resources for the VICs due to the early ++ * nature of the VIC's registration. ++*/ ++static int __init armctrl_syscore_init(void) ++{ ++ register_syscore_ops(&armctrl_syscore_ops); ++ return 0; ++} ++ ++late_initcall(armctrl_syscore_init); ++ ++#endif ++ ++static struct irq_chip armctrl_chip = { ++ .name = "ARMCTRL", ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38) ++ .irq_ack = armctrl_mask_irq, ++ .irq_mask = armctrl_mask_irq, ++ .irq_unmask = armctrl_unmask_irq, ++ .irq_set_wake = armctrl_set_wake, ++#else ++ .ack = armctrl_mask_irq, ++ .mask = armctrl_mask_irq, ++ .unmask = armctrl_unmask_irq, ++ .set_wake = armctrl_set_wake, ++#endif ++}; ++ ++/** ++ * armctrl_init - initialise a vectored interrupt controller ++ * @base: iomem base address ++ * @irq_start: starting interrupt number, must be muliple of 32 ++ * @armctrl_sources: bitmask of interrupt sources to allow ++ * @resume_sources: bitmask of interrupt sources to allow for resume ++ */ ++int __init armctrl_init(void __iomem * base, unsigned int irq_start, ++ u32 armctrl_sources, u32 resume_sources) ++{ ++ unsigned int irq; ++ ++ for (irq = 0; irq < NR_IRQS; irq++) { ++ unsigned int data = irq; ++ if (irq >= INTERRUPT_JPEG) ++ data = remap_irqs[irq - INTERRUPT_JPEG]; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38) ++ irq_set_chip(irq, &armctrl_chip); ++ irq_set_chip_data(irq, (void *)data); ++ irq_set_handler(irq, handle_level_irq); ++#else ++ set_irq_chip(irq, &armctrl_chip); ++ set_irq_chip_data(irq, (void *)data); ++ set_irq_handler(irq, handle_level_irq); ++#endif ++ set_irq_flags(irq, IRQF_VALID | IRQF_PROBE | IRQF_DISABLED); ++ } ++ ++ armctrl_pm_register(base, irq_start, resume_sources); ++ return 0; ++} +--- /dev/null ++++ b/arch/arm/mach-bcm2708/armctrl.h +@@ -0,0 +1,27 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/armctrl.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __BCM2708_ARMCTRL_H ++#define __BCM2708_ARMCTRL_H ++ ++extern int __init armctrl_init(void __iomem * base, unsigned int irq_start, ++ u32 armctrl_sources, u32 resume_sources); ++ ++#endif +--- /dev/null ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -0,0 +1,657 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/bcm2708.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <linux/init.h> ++#include <linux/device.h> ++#include <linux/dma-mapping.h> ++#include <linux/serial_8250.h> ++#include <linux/platform_device.h> ++#include <linux/interrupt.h> ++#include <linux/amba/bus.h> ++#include <linux/amba/clcd.h> ++#include <linux/clockchips.h> ++#include <linux/cnt32_to_63.h> ++#include <linux/io.h> ++ ++#include <linux/version.h> ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38) ++#include <linux/clkdev.h> ++#else ++#include <asm/clkdev.h> ++#endif ++#include <asm/system.h> ++#include <mach/hardware.h> ++#include <asm/irq.h> ++#include <linux/leds.h> ++#include <asm/mach-types.h> ++ ++#include <asm/mach/arch.h> ++#include <asm/mach/flash.h> ++#include <asm/mach/irq.h> ++#include <asm/mach/time.h> ++#include <asm/mach/map.h> ++ ++#include <mach/timex.h> ++#include <mach/dma.h> ++#include <mach/vcio.h> ++ ++#include "bcm2708.h" ++#include "armctrl.h" ++#include "clock.h" ++ ++/* Effectively we have an IOMMU (ARM<->VideoCore map) that is set up to ++ * give us IO access only to 64Mbytes of physical memory (26 bits). We could ++ * represent this window by setting our dmamasks to 26 bits but, in fact ++ * we're not going to use addresses outside this range (they're not in real ++ * memory) so we don't bother. ++ * ++ * In the future we might include code to use this IOMMU to remap other ++ * physical addresses onto VideoCore memory then the use of 32-bits would be ++ * more legitimate. ++ */ ++#define DMA_MASK_BITS_COMMON 32 ++ ++static void __init bcm2708_init_led(void); ++ ++void __init bcm2708_init_irq(void) ++{ ++ armctrl_init(__io_address(ARMCTRL_IC_BASE), 0, 0, 0); ++} ++ ++static struct map_desc bcm2708_io_desc[] __initdata = { ++ { ++ .virtual = IO_ADDRESS(ARMCTRL_BASE), ++ .pfn = __phys_to_pfn(ARMCTRL_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE ++ }, { ++ .virtual = IO_ADDRESS(UART0_BASE), ++ .pfn = __phys_to_pfn(UART0_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE ++ }, { ++ .virtual = IO_ADDRESS(UART1_BASE), ++ .pfn = __phys_to_pfn(UART1_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE ++ }, { ++#ifdef CONFIG_MMC_BCM2708 /* broadcom legacy SD */ ++ .virtual = IO_ADDRESS(MMCI0_BASE), ++ .pfn = __phys_to_pfn(MMCI0_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE ++ }, { ++#endif ++ .virtual = IO_ADDRESS(DMA_BASE), ++ .pfn = __phys_to_pfn(DMA_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE ++ }, { ++ .virtual = IO_ADDRESS(MCORE_BASE), ++ .pfn = __phys_to_pfn(MCORE_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE ++ }, { ++ .virtual = IO_ADDRESS(ST_BASE), ++ .pfn = __phys_to_pfn(ST_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE ++ }, { ++ .virtual = IO_ADDRESS(USB_BASE), ++ .pfn = __phys_to_pfn(USB_BASE), ++ .length = SZ_128K, ++ .type = MT_DEVICE ++ }, { ++ .virtual = IO_ADDRESS(PM_BASE), ++ .pfn = __phys_to_pfn(PM_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE ++ }, { ++ .virtual = IO_ADDRESS(GPIO_BASE), ++ .pfn = __phys_to_pfn(GPIO_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE ++ } ++}; ++ ++void __init bcm2708_map_io(void) ++{ ++ iotable_init(bcm2708_io_desc, ARRAY_SIZE(bcm2708_io_desc)); ++} ++ ++unsigned long frc_clock_ticks32(void) ++{ ++ /* STC: a free running counter that increments at the rate of 1MHz */ ++ return readl(__io_address(ST_BASE+0x04)); ++} ++ ++unsigned long long frc_clock_ticks63(void) ++{ ++ unsigned long t = frc_clock_ticks32(); ++ /* For cnt32_to_63 to work correctly we MUST call this routine ++ * at least once every half-32-bit-wraparound period - that's once ++ * every 35minutes or so - using it in sched_clock() should ensure this ++ */ ++ return cnt32_to_63(t); ++} ++ ++unsigned long long sched_clock(void) ++{ ++ return 1000ull * frc_clock_ticks63(); ++} ++ ++/* ++ * These are fixed clocks. ++ */ ++static struct clk ref24_clk = { ++ .rate = 3000000, /* The UART is clocked at 3MHz via APB_CLK */ ++}; ++static struct clk osc_clk = { ++#ifdef CONFIG_ARCH_BCM2708_CHIPIT ++ .rate = 27000000, ++#else ++ .rate = 500000000, /* ARM clock is set from the VideoCore booter */ ++#endif ++}; ++/* warning - the USB needs a clock > 34MHz */ ++ ++#ifdef CONFIG_MMC_BCM2708 ++static struct clk sdhost_clk = { ++#ifdef CONFIG_ARCH_BCM2708_CHIPIT ++ .rate = 4000000, /* 4MHz */ ++#else ++ .rate = 250000000, /* 250MHz */ ++#endif ++}; ++#endif ++ ++static struct clk_lookup lookups[] = { ++ { /* UART0 */ ++ .dev_id = "dev:f1", ++ .clk = &ref24_clk, ++ }, ++ { /* USB */ ++ .dev_id = "bcm2708_usb", ++ .clk = &osc_clk, ++#ifdef CONFIG_MMC_BCM2708 ++ }, ++ { /* MCI */ ++ .dev_id = "bcm2708_mci.0", ++ .clk = &sdhost_clk, ++#endif ++ } ++}; ++ ++ ++#define UART0_IRQ { IRQ_UART, NO_IRQ } ++#define UART0_DMA { 15, 14 } ++ ++AMBA_DEVICE(uart0, "dev:f1", UART0, NULL); ++ ++static struct amba_device *amba_devs[] __initdata = { ++ &uart0_device, ++}; ++ ++static struct resource bcm2708_dmaman_resources[] = { ++ { ++ .start = DMA_BASE, ++ .end = DMA_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ } ++}; ++ ++static struct platform_device bcm2708_dmaman_device = { ++ .name = BCM_DMAMAN_DRIVER_NAME, ++ .id = 0, /* first bcm2708_dma */ ++ .resource = bcm2708_dmaman_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_dmaman_resources), ++}; ++ ++#ifdef CONFIG_MMC_BCM2708 ++static struct resource bcm2708_mci_resources[] = { ++ { ++ .start = MMCI0_BASE, ++ .end = MMCI0_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = IRQ_SDIO, ++ .end = IRQ_SDIO, ++ .flags = IORESOURCE_IRQ, ++ } ++}; ++ ++ ++static struct platform_device bcm2708_mci_device = { ++ .name = "bcm2708_mci", ++ .id = 0, /* first bcm2708_mci */ ++ .resource = bcm2708_mci_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_mci_resources), ++ .dev = { ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++#endif /* CONFIG_MMC_BCM2708 */ ++ ++ ++static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_fb_device = { ++ .name = "bcm2708_fb", ++ .id = -1, /* only one bcm2708_fb */ ++ .resource = NULL, ++ .num_resources = 0, ++ .dev = { ++ .dma_mask = &fb_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++ ++static struct plat_serial8250_port bcm2708_uart1_platform_data[] = { ++ { ++ .mapbase = UART1_BASE + 0x40, ++ .irq = IRQ_AUX, ++ .uartclk = 125000000, ++ .regshift = 2, ++ .iotype = UPIO_MEM, ++ .flags = UPF_FIXED_TYPE | UPF_IOREMAP | UPF_SKIP_TEST, ++ .type = PORT_8250, ++ }, ++ { }, ++}; ++ ++static struct platform_device bcm2708_uart1_device = { ++ .name = "serial8250", ++ .id = PLAT8250_DEV_PLATFORM, ++ .dev = { ++ .platform_data = bcm2708_uart1_platform_data, ++ }, ++}; ++ ++static struct resource bcm2708_usb_resources[] = { ++ [0] = { ++ .start = USB_BASE, ++ .end = USB_BASE + SZ_128K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_USB, ++ .end = IRQ_USB, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_usb_device = { ++ .name = "bcm2708_usb", ++ .id = -1, /* only one bcm2708_usb */ ++ .resource = bcm2708_usb_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_usb_resources), ++ .dev = { ++ .dma_mask = &usb_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++ ++static struct resource bcm2708_vcio_resources[] = { ++ [0] = { /* mailbox/semaphore/doorbell access */ ++ .start = MCORE_BASE, ++ .end = MCORE_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static u64 vcio_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_vcio_device = { ++ .name = BCM_VCIO_DRIVER_NAME, ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_vcio_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_vcio_resources), ++ .dev = { ++ .dma_mask = &vcio_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++ ++#ifdef CONFIG_BCM2708_GPIO ++#define BCM_GPIO_DRIVER_NAME "bcm2708_gpio" ++ ++static struct resource bcm2708_gpio_resources[] = { ++ [0] = { /* general purpose I/O */ ++ .start = GPIO_BASE, ++ .end = GPIO_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static u64 gpio_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_gpio_device = { ++ .name = BCM_GPIO_DRIVER_NAME, ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_gpio_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_gpio_resources), ++ .dev = { ++ .dma_mask = &gpio_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++#endif ++ ++#ifdef CONFIG_BCM2708_BUTTONS ++static struct resource bcm2708_vcbuttons_resources[] = { ++}; ++ ++static u64 vcbuttons_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_vcbuttons_device = { ++ .name = "bcm2708_vcbuttons", ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_vcbuttons_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_vcbuttons_resources), ++ .dev = { ++ .dma_mask = &vcbuttons_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++#endif ++ ++#ifdef CONFIG_BCM2708_TOUCHSCREEN ++static struct resource bcm2708_vctouch_resources[] = { ++}; ++ ++static u64 vctouch_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_vctouch_device = { ++ .name = "bcm2708_vctouch", ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_vctouch_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_vctouch_resources), ++ .dev = { ++ .dma_mask = &vctouch_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++#endif ++ ++static struct resource bcm2708_systemtimer_resources[] = { ++ [0] = { /* system timer access */ ++ .start = ST_BASE, ++ .end = ST_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, { ++ .start = IRQ_TIMER3, ++ .end = IRQ_TIMER3, ++ .flags = IORESOURCE_IRQ, ++ } ++ ++ ++}; ++ ++static u64 systemtimer_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++static struct platform_device bcm2708_systemtimer_device = { ++ .name = "bcm2708_systemtimer", ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_systemtimer_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_systemtimer_resources), ++ .dev = { ++ .dma_mask = &systemtimer_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, ++}; ++ ++#ifdef CONFIG_MMC_SDHCI_BCM2708 /* Arasan emmc SD */ ++static struct resource bcm2708_emmc_resources[] = { ++ [0] = { ++ .start = EMMC_BASE, ++ .end = EMMC_BASE + SZ_256 - 1, /* we only need this area */ ++ /* the memory map actually makes SZ_4K available */ ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_ARASANSDIO, ++ .end = IRQ_ARASANSDIO, ++ .flags = IORESOURCE_IRQ, ++ }, ++}; ++ ++static u64 bcm2708_emmc_dmamask = 0xffffffffUL; ++ ++struct platform_device bcm2708_emmc_device = { ++ .name = "bcm2708_sdhci", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_emmc_resources), ++ .resource = bcm2708_emmc_resources, ++ .dev = { ++ .dma_mask = &bcm2708_emmc_dmamask, ++ .coherent_dma_mask = 0xffffffffUL ++ }, ++}; ++#endif /* CONFIG_MMC_SDHCI_BCM2708 */ ++ ++static struct resource bcm2708_powerman_resources[] = { ++ [0] = { ++ .start = PM_BASE, ++ .end = PM_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++}; ++ ++static u64 powerman_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); ++ ++struct platform_device bcm2708_powerman_device = { ++ .name = "bcm2708_powerman", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_powerman_resources), ++ .resource = bcm2708_powerman_resources, ++ .dev = { ++ .dma_mask = &powerman_dmamask, ++ .coherent_dma_mask = 0xffffffffUL ++ }, ++}; ++ ++int __init bcm_register_device(struct platform_device *pdev) ++{ ++ int ret; ++ ++ ret = platform_device_register(pdev); ++ if (ret) ++ pr_debug("Unable to register platform device '%s': %d\n", ++ pdev->name, ret); ++ ++ return ret; ++} ++ ++void __init bcm2708_init(void) ++{ ++ int i; ++ ++ for (i = 0; i < ARRAY_SIZE(lookups); i++) ++ clkdev_add(&lookups[i]); ++ ++ bcm_register_device(&bcm2708_dmaman_device); ++ bcm_register_device(&bcm2708_vcio_device); ++#ifdef CONFIG_BCM2708_GPIO ++ bcm_register_device(&bcm2708_gpio_device); ++#endif ++ bcm_register_device(&bcm2708_systemtimer_device); ++#ifdef CONFIG_MMC_BCM2708 ++ bcm_register_device(&bcm2708_mci_device); ++#endif ++ bcm_register_device(&bcm2708_fb_device); ++ bcm_register_device(&bcm2708_usb_device); ++ bcm_register_device(&bcm2708_uart1_device); ++#ifdef CONFIG_BCM2708_BUTTONS ++ bcm_register_device(&bcm2708_vcbuttons_device); ++#endif ++#ifdef CONFIG_BCM2708_TOUCHSCREEN ++ bcm_register_device(&bcm2708_vctouch_device); ++#endif ++ bcm_register_device(&bcm2708_powerman_device); ++#ifdef CONFIG_MMC_SDHCI_BCM2708 ++ bcm_register_device(&bcm2708_emmc_device); ++#endif ++ bcm2708_init_led(); ++#ifdef CONFIG_BCM2708_VCMEM ++{ ++ extern void vc_mem_connected_init(void); ++ vc_mem_connected_init(); ++} ++#endif ++ for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { ++ struct amba_device *d = amba_devs[i]; ++ amba_device_register(d, &iomem_resource); ++ } ++} ++ ++#define TIMER_PERIOD 10000 /* HZ in microsecs */ ++ ++static void timer_set_mode(enum clock_event_mode mode, ++ struct clock_event_device *clk) ++{ ++ unsigned long stc; ++ ++ switch (mode) { ++ case CLOCK_EVT_MODE_PERIODIC: ++ stc = readl(__io_address(ST_BASE+0x04)); ++ writel(stc + TIMER_PERIOD, ++ __io_address(ST_BASE+0x18));/* stc3 */ ++ break; ++ case CLOCK_EVT_MODE_ONESHOT: ++ case CLOCK_EVT_MODE_UNUSED: ++ case CLOCK_EVT_MODE_SHUTDOWN: ++ default: ++ printk(KERN_ERR "timer_set_mode: unhandled mode:%d\n", ++ (int)mode); ++ break; ++ } ++ ++} ++ ++static int timer_set_next_event(unsigned long evt, ++ struct clock_event_device *unused) ++{ ++ unsigned long stc; ++ ++ stc = readl(__io_address(ST_BASE + 0x04)); ++ writel(stc + TIMER_PERIOD, __io_address(ST_BASE+0x18)); /* stc3 */ ++ return 0; ++} ++ ++static struct clock_event_device timer0_clockevent = { ++ .name = "timer0", ++ .shift = 32, ++ .features = CLOCK_EVT_FEAT_ONESHOT, ++ .set_mode = timer_set_mode, ++ .set_next_event = timer_set_next_event, ++}; ++ ++/* ++ * IRQ handler for the timer ++ */ ++static irqreturn_t bcm2708_timer_interrupt(int irq, void *dev_id) ++{ ++ struct clock_event_device *evt = &timer0_clockevent; ++ ++ writel(1<<3, __io_address(ST_BASE+0x00)); /* stcs clear timer int */ ++ ++ evt->event_handler(evt); ++ ++ return IRQ_HANDLED; ++} ++ ++static struct irqaction bcm2708_timer_irq = { ++ .name = "BCM2708 Timer Tick", ++ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, ++ .handler = bcm2708_timer_interrupt, ++}; ++ ++/* ++ * Set up timer interrupt, and return the current time in seconds. ++ */ ++static void __init bcm2708_timer_init(void) ++{ ++ /* ++ * Initialise to a known state (all timers off) ++ */ ++ writel(0, __io_address(ARM_T_CONTROL)); ++ /* ++ * Make irqs happen for the system timer ++ */ ++ setup_irq(IRQ_TIMER3, &bcm2708_timer_irq); ++ ++ timer0_clockevent.mult = ++ div_sc(1000000, NSEC_PER_SEC, timer0_clockevent.shift); ++ timer0_clockevent.max_delta_ns = ++ clockevent_delta2ns(0xffffffff, &timer0_clockevent); ++ timer0_clockevent.min_delta_ns = ++ clockevent_delta2ns(0xf, &timer0_clockevent); ++ ++ timer0_clockevent.cpumask = cpumask_of(0); ++ clockevents_register_device(&timer0_clockevent); ++} ++ ++struct sys_timer bcm2708_timer = { ++ .init = bcm2708_timer_init, ++}; ++ ++#if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) ++#include <linux/leds.h> ++ ++static struct gpio_led bcm2708_leds[] = { ++ [0] = { ++ .gpio = 16, ++ .name = "led0", ++ .default_trigger = "mmc0", ++ .active_low = 0, ++ }, ++}; ++ ++static struct gpio_led_platform_data bcm2708_led_pdata = { ++ .num_leds = ARRAY_SIZE(bcm2708_leds), ++ .leds = bcm2708_leds, ++}; ++ ++static struct platform_device bcm2708_led_device = { ++ .name = "leds-gpio", ++ .id = -1, ++ .dev = { ++ .platform_data = &bcm2708_led_pdata, ++ }, ++}; ++ ++static void __init bcm2708_init_led(void) ++{ ++ platform_device_register(&bcm2708_led_device); ++} ++#else ++static inline void bcm2708_init_led(void) {} ++#endif ++ ++ ++MACHINE_START(BCM2708, "BCM2708") ++ /* Maintainer: Broadcom Europe Ltd. */ ++ .map_io = bcm2708_map_io, ++ .init_irq = bcm2708_init_irq, ++ .timer = &bcm2708_timer, ++ .init_machine = bcm2708_init, ++MACHINE_END +--- /dev/null ++++ b/arch/arm/mach-bcm2708/bcm2708.h +@@ -0,0 +1,51 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/bcm2708.h ++ * ++ * BCM2708 machine support header ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __BCM2708_BCM2708_H ++#define __BCM2708_BCM2708_H ++ ++#include <linux/amba/bus.h> ++ ++extern void __init bcm2708_init(void); ++extern void __init bcm2708_init_irq(void); ++extern void __init bcm2708_map_io(void); ++extern struct sys_timer bcm2708_timer; ++extern unsigned int mmc_status(struct device *dev); ++ ++#define AMBA_DEVICE(name, busid, base, plat) \ ++static struct amba_device name##_device = { \ ++ .dev = { \ ++ .coherent_dma_mask = ~0, \ ++ .init_name = busid, \ ++ .platform_data = plat, \ ++ }, \ ++ .res = { \ ++ .start = base##_BASE, \ ++ .end = (base##_BASE) + SZ_4K - 1,\ ++ .flags = IORESOURCE_MEM, \ ++ }, \ ++ .dma_mask = ~0, \ ++ .irq = base##_IRQ, \ ++ /* .dma = base##_DMA,*/ \ ++} ++ ++#endif +--- /dev/null ++++ b/arch/arm/mach-bcm2708/bcm2708_gpio.c +@@ -0,0 +1,323 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/bcm2708_gpio.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ */ ++ ++#include <linux/spinlock.h> ++#include <linux/module.h> ++#include <linux/list.h> ++#include <linux/io.h> ++#include <linux/irq.h> ++#include <linux/slab.h> ++#include <linux/gpio.h> ++#include <linux/platform_device.h> ++#include <mach/platform.h> ++#include <mach/gpio.h> ++ ++#define BCM_GPIO_DRIVER_NAME "bcm2708_gpio" ++#define DRIVER_NAME BCM_GPIO_DRIVER_NAME ++#define BCM_GPIO_USE_IRQ 0 ++ ++#define GPIOFSEL(x) (0x00+(x)*4) ++#define GPIOSET(x) (0x1c+(x)*4) ++#define GPIOCLR(x) (0x28+(x)*4) ++#define GPIOLEV(x) (0x34+(x)*4) ++#define GPIOEDS(x) (0x40+(x)*4) ++#define GPIOREN(x) (0x4c+(x)*4) ++#define GPIOFEN(x) (0x58+(x)*4) ++#define GPIOHEN(x) (0x64+(x)*4) ++#define GPIOLEN(x) (0x70+(x)*4) ++#define GPIOAREN(x) (0x7c+(x)*4) ++#define GPIOAFEN(x) (0x88+(x)*4) ++#define GPIOUD(x) (0x94+(x)*4) ++#define GPIOUDCLK(x) (0x98+(x)*4) ++ ++enum { GPIO_FSEL_INPUT, GPIO_FSEL_OUTPUT, ++ GPIO_FSEL_ALT5, GPIO_FSEL_ALT_4, ++ GPIO_FSEL_ALT0, GPIO_FSEL_ALT1, ++ GPIO_FSEL_ALT2, GPIO_FSEL_ALT3, }; ++ ++ /* Each of the two spinlocks protects a different set of hardware ++ * regiters and data structurs. This decouples the code of the IRQ from ++ * the GPIO code. This also makes the case of a GPIO routine call from ++ * the IRQ code simpler. ++ */ ++static DEFINE_SPINLOCK(lock); /* GPIO registers */ ++static DEFINE_SPINLOCK(irq_lock); /* IRQ registers */ ++ ++ ++struct bcm2708_gpio { ++ /* We use a list of bcm2708_gpio structs for each trigger IRQ in the main ++ * interrupts controller of the system. We need this to support systems ++ * in which more that one bcm2708s are connected to the same IRQ. The ISR ++ * interates through this list to find the source of the interrupt. ++ */ ++ struct list_head list; ++ ++ void __iomem *base; ++ unsigned irq_base; ++ struct gpio_chip gc; ++}; ++ ++static int bcm2708_set_function(struct gpio_chip *gc, unsigned offset, int function) ++{ ++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); ++ unsigned long flags; ++ unsigned gpiodir; ++ unsigned gpio_bank = offset/10; ++ unsigned gpio_field_offset = (offset - 10*gpio_bank) * 3; ++ ++//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set_function %p (%d,%d)\n", gc, offset, function); ++ if (offset >= ARCH_NR_GPIOS) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&lock, flags); ++ ++ gpiodir = readl(gpio->base + GPIOFSEL(gpio_bank)); ++ gpiodir &= ~(7 << gpio_field_offset); ++ gpiodir |= function << gpio_field_offset; ++ writel(gpiodir, gpio->base + GPIOFSEL(gpio_bank)); ++ spin_unlock_irqrestore(&lock, flags); ++ gpiodir = readl(gpio->base + GPIOFSEL(gpio_bank)); ++ ++ return 0; ++} ++ ++ ++static int bcm2708_gpio_dir_in(struct gpio_chip *gc, unsigned offset) ++{ ++ return bcm2708_set_function(gc, offset, GPIO_FSEL_INPUT); ++} ++ ++static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value); ++static int bcm2708_gpio_dir_out(struct gpio_chip *gc, unsigned offset, int value) ++{ ++ int ret; ++ ret = bcm2708_set_function(gc, offset, GPIO_FSEL_OUTPUT); ++ if (ret >= 0) ++ bcm2708_gpio_set(gc, offset, value); ++ return ret; ++} ++ ++static int bcm2708_gpio_get(struct gpio_chip *gc, unsigned offset) ++{ ++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); ++ unsigned gpio_bank = offset/32; ++ unsigned gpio_field_offset = (offset - 32*gpio_bank); ++ unsigned lev; ++ ++ if (offset >= ARCH_NR_GPIOS) ++ return 0; ++ lev = readl(gpio->base + GPIOLEV(gpio_bank)); ++//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_get %p (%d)=%d\n", gc, offset, 0x1 & (lev>>gpio_field_offset)); ++ return 0x1 & (lev>>gpio_field_offset); ++} ++ ++static void bcm2708_gpio_set(struct gpio_chip *gc, unsigned offset, int value) ++{ ++ struct bcm2708_gpio *gpio = container_of(gc, struct bcm2708_gpio, gc); ++ unsigned gpio_bank = offset/32; ++ unsigned gpio_field_offset = (offset - 32*gpio_bank); ++//printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_set %p (%d=%d)\n", gc, offset, value); ++ if (offset >= ARCH_NR_GPIOS) ++ return; ++ if (value) ++ writel(1<<gpio_field_offset, gpio->base + GPIOSET(gpio_bank)); ++ else ++ writel(1<<gpio_field_offset, gpio->base + GPIOCLR(gpio_bank)); ++} ++ ++/* ++ * bcm2708 GPIO IRQ ++ */ ++ ++#if BCM_GPIO_USE_IRQ ++static void bcm2708_irq_disable(unsigned irq) ++{ ++ struct bcm2708_gpio *chip = get_irq_chip_data(irq); ++ //int offset = irq - gpio->irq_base; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&chip->irq_lock, flags); ++ // disable gpio interrupts here ++ spin_unlock_irqrestore(&chip->irq_lock, flags); ++} ++ ++static void bcm2708_irq_enable(unsigned irq) ++{ ++ struct bcm2708_gpio *chip = get_irq_chip_data(irq); ++ //int offset = irq - chip->irq_base; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&chip->irq_lock, flags); ++ // enable gpio interrupts here ++ spin_unlock_irqrestore(&chip->irq_lock, flags); ++} ++ ++static int bcm2708_irq_type(unsigned irq, unsigned trigger) ++{ ++ struct bcm2708_gpio *chip = get_irq_chip_data(irq); ++ int offset = irq - chip->irq_base; ++ unsigned long flags; ++ unsigned gpio_bank = offset/32; ++ unsigned gpio_field_offset = (offset - 32*gpio_bank); ++ unsigned gpioren, gpiofen, gpiohen, gpiolen; ++ ++ if (offset < 0 || offset >= ARCH_NR_GPIOS) ++ return -EINVAL; ++ ++ spin_lock_irqsave(&chip->irq_lock, flags); ++ ++ gpioren = readl(chip->base + GPIOREN(gpio_bank)); ++ gpiofen = readl(chip->base + GPIOFEN(gpio_bank)); ++ gpiohen = readl(chip->base + GPIOHEN(gpio_bank)); ++ gpiolen = readl(chip->base + GPIOLEN(gpio_bank)); ++ ++ if (trigger & (IRQ_TYPE_EDGE_RISING)) ++ gpioren |= (1<<gpio_field_offset); ++ else ++ gpioren &= ~(1<<gpio_field_offset); ++ if (trigger & (IRQ_TYPE_EDGE_FALLING)) ++ gpiofen |= (1<<gpio_field_offset); ++ else ++ gpiofen &= ~(1<<gpio_field_offset); ++ if (trigger & (IRQ_TYPE_LEVEL_HIGH)) ++ gpiohen |= (1<<gpio_field_offset); ++ else ++ gpiohen &= ~(1<<gpio_field_offset); ++ if (trigger & (IRQ_TYPE_LEVEL_LOW)) ++ gpiolen |= (1<<gpio_field_offset); ++ else ++ gpiolen &= ~(1<<gpio_field_offset); ++ ++ writel(gpioren, chip->base + GPIOREN(gpio_bank)); ++ writel(gpiofen, chip->base + GPIOFEN(gpio_bank)); ++ writel(gpiohen, chip->base + GPIOHEN(gpio_bank)); ++ writel(gpiolen, chip->base + GPIOLEN(gpio_bank)); ++ ++ spin_unlock_irqrestore(&chip->irq_lock, flags); ++ ++ return 0; ++} ++ ++static struct irq_chip bcm2708_irqchip = { ++ .name = "GPIO", ++ .enable = bcm2708_irq_enable, ++ .disable = bcm2708_irq_disable, ++ .set_type = bcm2708_irq_type, ++}; ++ ++static void bcm2708_irq_handler(unsigned irq, struct irq_desc *desc) ++{ ++ struct list_head *chip_list = get_irq_data(irq); ++ struct list_head *ptr; ++ struct bcm2708_gpio *chip; ++ unsigned gpio_bank; ++ ++ desc->chip->ack(irq); ++ list_for_each(ptr, chip_list) { ++ unsigned long pending; ++ int offset; ++ ++ chip = list_entry(ptr, struct bcm2708_gpio, list); ++ for (gpio_bank = 0; gpio_bank < ARCH_NR_GPIOS/32; gpio_bank++) { ++ pending = readl(chip->base + GPIOEDS(gpio_bank)); ++ writel(pending, chip->base + GPIOEDS(gpio_bank)); ++ ++ if (pending == 0) ++ continue; ++ ++ for_each_set_bit(offset, &pending, ARCH_NR_GPIOS) ++ generic_handle_irq(gpio_to_irq(offset+32*gpio_bank)); ++ } ++ } ++ desc->chip->unmask(irq); ++} ++#endif /* #if BCM_GPIO_USE_IRQ */ ++ ++static int bcm2708_gpio_probe(struct platform_device *dev) ++{ ++ struct bcm2708_gpio *ucb; ++ struct resource *res; ++ int err = 0; ++ ++ printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_probe %p\n", dev); ++ ++ ucb = kzalloc(sizeof(*ucb), GFP_KERNEL); ++ if (NULL == ucb) { ++ printk(KERN_ERR DRIVER_NAME ": failed to allocate " ++ "mailbox memory\n"); ++ err = -ENOMEM; ++ goto err; ++ } ++ ++ res = platform_get_resource(dev, IORESOURCE_MEM, 0); ++ ++ platform_set_drvdata(dev, ucb); ++ ucb->base = __io_address(GPIO_BASE); ++ ++ ucb->gc.label = "bcm2708_gpio"; ++ ucb->gc.base = 0; ++ ucb->gc.ngpio = ARCH_NR_GPIOS; ++ ucb->gc.owner = THIS_MODULE; ++ ++ ucb->gc.direction_input = bcm2708_gpio_dir_in; ++ ucb->gc.direction_output = bcm2708_gpio_dir_out; ++ ucb->gc.get = bcm2708_gpio_get; ++ ucb->gc.set = bcm2708_gpio_set; ++ ucb->gc.can_sleep = 0; ++ ++ err = gpiochip_add(&ucb->gc); ++ if (err) ++ goto err; ++ ++err: ++ return err; ++ ++} ++ ++static int bcm2708_gpio_remove(struct platform_device *dev) ++{ ++ int err = 0; ++ struct bcm2708_gpio *ucb = platform_get_drvdata(dev); ++ ++ printk(KERN_ERR DRIVER_NAME ": bcm2708_gpio_remove %p\n", dev); ++ ++ err = gpiochip_remove(&ucb->gc); ++ ++ platform_set_drvdata(dev, NULL); ++ kfree(ucb); ++ ++ return err; ++} ++ ++static struct platform_driver bcm2708_gpio_driver = { ++ .probe = bcm2708_gpio_probe, ++ .remove = bcm2708_gpio_remove, ++ .driver = { ++ .name = "bcm2708_gpio" ++ }, ++}; ++ ++static int __init bcm2708_gpio_init(void) ++{ ++ return platform_driver_register(&bcm2708_gpio_driver); ++} ++ ++static void __exit bcm2708_gpio_exit(void) ++{ ++ platform_driver_unregister(&bcm2708_gpio_driver); ++} ++ ++module_init(bcm2708_gpio_init); ++module_exit(bcm2708_gpio_exit); ++ ++MODULE_DESCRIPTION("Broadcom BCM2708 GPIO driver"); ++MODULE_LICENSE("GPL"); ++ +--- /dev/null ++++ b/arch/arm/mach-bcm2708/clock.c +@@ -0,0 +1,61 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/clock.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/device.h> ++#include <linux/list.h> ++#include <linux/errno.h> ++#include <linux/err.h> ++#include <linux/string.h> ++#include <linux/clk.h> ++#include <linux/mutex.h> ++ ++#include <asm/clkdev.h> ++ ++#include "clock.h" ++ ++int clk_enable(struct clk *clk) ++{ ++ return 0; ++} ++EXPORT_SYMBOL(clk_enable); ++ ++void clk_disable(struct clk *clk) ++{ ++} ++EXPORT_SYMBOL(clk_disable); ++ ++unsigned long clk_get_rate(struct clk *clk) ++{ ++ return clk->rate; ++} ++EXPORT_SYMBOL(clk_get_rate); ++ ++long clk_round_rate(struct clk *clk, unsigned long rate) ++{ ++ return clk->rate; ++} ++EXPORT_SYMBOL(clk_round_rate); ++ ++int clk_set_rate(struct clk *clk, unsigned long rate) ++{ ++ return -EIO; ++} ++EXPORT_SYMBOL(clk_set_rate); +--- /dev/null ++++ b/arch/arm/mach-bcm2708/clock.h +@@ -0,0 +1,24 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/clock.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++struct module; ++ ++struct clk { ++ unsigned long rate; ++}; +--- /dev/null ++++ b/arch/arm/mach-bcm2708/dma.c +@@ -0,0 +1,397 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/dma.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/slab.h> ++#include <linux/device.h> ++#include <linux/platform_device.h> ++#include <linux/module.h> ++#include <linux/scatterlist.h> ++ ++#include <mach/dma.h> ++#include <mach/irqs.h> ++ ++/*****************************************************************************\ ++ * * ++ * Configuration * ++ * * ++\*****************************************************************************/ ++ ++#define CACHE_LINE_MASK 31 ++#define DRIVER_NAME BCM_DMAMAN_DRIVER_NAME ++#define DEFAULT_DMACHAN_BITMAP 0x10 /* channel 4 only */ ++ ++/* valid only for channels 0 - 14, 15 has its own base address */ ++#define BCM2708_DMA_CHAN(n) ((n)<<8) /* base address */ ++#define BCM2708_DMA_CHANIO(dma_base, n) \ ++ ((void __iomem *)((char *)(dma_base)+BCM2708_DMA_CHAN(n))) ++ ++ ++/*****************************************************************************\ ++ * * ++ * DMA Auxilliary Functions * ++ * * ++\*****************************************************************************/ ++ ++/* A DMA buffer on an arbitrary boundary may separate a cache line into a ++ section inside the DMA buffer and another section outside it. ++ Even if we flush DMA buffers from the cache there is always the chance that ++ during a DMA someone will access the part of a cache line that is outside ++ the DMA buffer - which will then bring in unwelcome data. ++ Without being able to dictate our own buffer pools we must insist that ++ DMA buffers consist of a whole number of cache lines. ++*/ ++ ++extern int ++bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len) ++{ ++ int i; ++ ++ for (i = 0; i < sg_len; i++) { ++ if (sg_ptr[i].offset & CACHE_LINE_MASK || ++ sg_ptr[i].length & CACHE_LINE_MASK) ++ return 0; ++ } ++ ++ return 1; ++} ++EXPORT_SYMBOL_GPL(bcm_sg_suitable_for_dma); ++ ++extern void ++bcm_dma_start(void __iomem *dma_chan_base, dma_addr_t control_block) ++{ ++ dsb(); /* ARM data synchronization (push) operation */ ++ ++ writel(control_block, dma_chan_base + BCM2708_DMA_ADDR); ++ writel(BCM2708_DMA_ACTIVE, dma_chan_base + BCM2708_DMA_CS); ++} ++ ++extern void bcm_dma_wait_idle(void __iomem *dma_chan_base) ++{ ++ dsb(); ++ ++ /* ugly busy wait only option for now */ ++ while (readl(dma_chan_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE); ++} ++ ++EXPORT_SYMBOL_GPL(bcm_dma_start); ++ ++/* Complete an ongoing DMA (assuming its results are to be ignored) ++ Does nothing if there is no DMA in progress. ++ This routine waits for the current AXI transfer to complete before ++ terminating the current DMA. If the current transfer is hung on a DREQ used ++ by an uncooperative peripheral the AXI transfer may never complete. In this ++ case the routine times out and return a non-zero error code. ++ Use of this routine doesn't guarantee that the ongoing or aborted DMA ++ does not produce an interrupt. ++*/ ++extern int ++bcm_dma_abort(void __iomem *dma_chan_base) ++{ ++ unsigned long int cs; ++ int rc = 0; ++ ++ cs = readl(dma_chan_base + BCM2708_DMA_CS); ++ ++ if (BCM2708_DMA_ACTIVE & cs) { ++ long int timeout = 10000; ++ ++ /* write 0 to the active bit - pause the DMA */ ++ writel(0, dma_chan_base + BCM2708_DMA_CS); ++ ++ /* wait for any current AXI transfer to complete */ ++ while (0 != (cs & BCM2708_DMA_ISPAUSED) && --timeout >= 0) ++ cs = readl(dma_chan_base + BCM2708_DMA_CS); ++ ++ if (0 != (cs & BCM2708_DMA_ISPAUSED)) { ++ /* we'll un-pause when we set of our next DMA */ ++ rc = -ETIMEDOUT; ++ ++ } else if (BCM2708_DMA_ACTIVE & cs) { ++ /* terminate the control block chain */ ++ writel(0, dma_chan_base + BCM2708_DMA_NEXTCB); ++ ++ /* abort the whole DMA */ ++ writel(BCM2708_DMA_ABORT | BCM2708_DMA_ACTIVE, ++ dma_chan_base + BCM2708_DMA_CS); ++ } ++ } ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL(bcm_dma_abort); ++ ++ ++/***************************************************************************** \ ++ * * ++ * DMA Manager Device Methods * ++ * * ++\*****************************************************************************/ ++ ++struct vc_dmaman { ++ void __iomem *dma_base; ++ u32 chan_available; /* bitmap of available channels */ ++ u32 has_feature[BCM_DMA_FEATURE_COUNT]; /* bitmap of feature presence */ ++}; ++ ++static void vc_dmaman_init(struct vc_dmaman *dmaman, void __iomem *dma_base, ++ u32 chans_available) ++{ ++ dmaman->dma_base = dma_base; ++ dmaman->chan_available = chans_available; ++ dmaman->has_feature[BCM_DMA_FEATURE_FAST_ORD] = 0x0c; /* chans 2 & 3 */ ++} ++ ++static int vc_dmaman_chan_alloc(struct vc_dmaman *dmaman, ++ unsigned preferred_feature_set) ++{ ++ u32 chans; ++ int feature; ++ ++ chans = dmaman->chan_available; ++ for (feature = 0; feature < BCM_DMA_FEATURE_COUNT; feature++) ++ /* select the subset of available channels with the desired ++ feature so long as some of the candidate channels have that ++ feature */ ++ if ((preferred_feature_set & (1 << feature)) && ++ (chans & dmaman->has_feature[feature])) ++ chans &= dmaman->has_feature[feature]; ++ ++ if (chans) { ++ int chan = 0; ++ /* return the ordinal of the first channel in the bitmap */ ++ while (chans != 0 && (chans & 1) == 0) { ++ chans >>= 1; ++ chan++; ++ } ++ /* claim the channel */ ++ dmaman->chan_available &= ~(1 << chan); ++ return chan; ++ } else ++ return -ENOMEM; ++} ++ ++static int vc_dmaman_chan_free(struct vc_dmaman *dmaman, int chan) ++{ ++ if (chan < 0) ++ return -EINVAL; ++ else if ((1 << chan) & dmaman->chan_available) ++ return -EIDRM; ++ else { ++ dmaman->chan_available |= (1 << chan); ++ return 0; ++ } ++} ++ ++/*****************************************************************************\ ++ * * ++ * DMA IRQs * ++ * * ++\*****************************************************************************/ ++ ++static unsigned char bcm_dma_irqs[] = { ++ IRQ_DMA0, ++ IRQ_DMA1, ++ IRQ_DMA2, ++ IRQ_DMA3, ++ IRQ_DMA4, ++ IRQ_DMA5, ++ IRQ_DMA6, ++ IRQ_DMA7, ++ IRQ_DMA8, ++ IRQ_DMA9, ++ IRQ_DMA10, ++ IRQ_DMA11, ++ IRQ_DMA12 ++}; ++ ++ ++/***************************************************************************** \ ++ * * ++ * DMA Manager Monitor * ++ * * ++\*****************************************************************************/ ++ ++static struct device *dmaman_dev; /* we assume there's only one! */ ++ ++extern int bcm_dma_chan_alloc(unsigned preferred_feature_set, ++ void __iomem **out_dma_base, int *out_dma_irq) ++{ ++ if (!dmaman_dev) ++ return -ENODEV; ++ else { ++ struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); ++ int rc; ++ ++ device_lock(dmaman_dev); ++ rc = vc_dmaman_chan_alloc(dmaman, preferred_feature_set); ++ if (rc >= 0) { ++ *out_dma_base = BCM2708_DMA_CHANIO(dmaman->dma_base, ++ rc); ++ *out_dma_irq = bcm_dma_irqs[rc]; ++ } ++ device_unlock(dmaman_dev); ++ ++ return rc; ++ } ++} ++EXPORT_SYMBOL_GPL(bcm_dma_chan_alloc); ++ ++extern int bcm_dma_chan_free(int channel) ++{ ++ if (dmaman_dev) { ++ struct vc_dmaman *dmaman = dev_get_drvdata(dmaman_dev); ++ int rc; ++ ++ device_lock(dmaman_dev); ++ rc = vc_dmaman_chan_free(dmaman, channel); ++ device_unlock(dmaman_dev); ++ ++ return rc; ++ } else ++ return -ENODEV; ++} ++EXPORT_SYMBOL_GPL(bcm_dma_chan_free); ++ ++static int dev_dmaman_register(const char *dev_name, struct device *dev) ++{ ++ int rc = dmaman_dev ? -EINVAL : 0; ++ dmaman_dev = dev; ++ return rc; ++} ++ ++static void dev_dmaman_deregister(const char *dev_name, struct device *dev) ++{ ++ dmaman_dev = NULL; ++} ++ ++/*****************************************************************************\ ++ * * ++ * DMA Device * ++ * * ++\*****************************************************************************/ ++ ++static int dmachans = -1; /* module parameter */ ++ ++static int bcm_dmaman_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ struct vc_dmaman *dmaman; ++ struct resource *dma_res = NULL; ++ void __iomem *dma_base = NULL; ++ int have_dma_region = 0; ++ ++ dmaman = kzalloc(sizeof(*dmaman), GFP_KERNEL); ++ if (NULL == dmaman) { ++ printk(KERN_ERR DRIVER_NAME ": failed to allocate " ++ "DMA management memory\n"); ++ ret = -ENOMEM; ++ } else { ++ ++ dma_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (dma_res == NULL) { ++ printk(KERN_ERR DRIVER_NAME ": failed to obtain memory " ++ "resource\n"); ++ ret = -ENODEV; ++ } else if (!request_mem_region(dma_res->start, ++ resource_size(dma_res), ++ DRIVER_NAME)) { ++ dev_err(&pdev->dev, "cannot obtain DMA region\n"); ++ ret = -EBUSY; ++ } else { ++ have_dma_region = 1; ++ dma_base = ioremap(dma_res->start, ++ resource_size(dma_res)); ++ if (!dma_base) { ++ dev_err(&pdev->dev, "cannot map DMA region\n"); ++ ret = -ENOMEM; ++ } else { ++ /* use module parameter if one was provided */ ++ if (dmachans > 0) ++ vc_dmaman_init(dmaman, dma_base, ++ dmachans); ++ else ++ vc_dmaman_init(dmaman, dma_base, ++ DEFAULT_DMACHAN_BITMAP); ++ ++ platform_set_drvdata(pdev, dmaman); ++ dev_dmaman_register(DRIVER_NAME, &pdev->dev); ++ ++ printk(KERN_INFO DRIVER_NAME ": DMA manager " ++ "at %p\n", dma_base); ++ } ++ } ++ } ++ if (ret != 0) { ++ if (dma_base) ++ iounmap(dma_base); ++ if (dma_res && have_dma_region) ++ release_mem_region(dma_res->start, ++ resource_size(dma_res)); ++ if (dmaman) ++ kfree(dmaman); ++ } ++ return ret; ++} ++ ++static int bcm_dmaman_remove(struct platform_device *pdev) ++{ ++ struct vc_dmaman *dmaman = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ dev_dmaman_deregister(DRIVER_NAME, &pdev->dev); ++ kfree(dmaman); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm_dmaman_driver = { ++ .probe = bcm_dmaman_probe, ++ .remove = bcm_dmaman_remove, ++ ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++/*****************************************************************************\ ++ * * ++ * Driver init/exit * ++ * * ++\*****************************************************************************/ ++ ++static int __init bcm_dmaman_drv_init(void) ++{ ++ int ret; ++ ++ ret = platform_driver_register(&bcm_dmaman_driver); ++ if (ret != 0) { ++ printk(KERN_ERR DRIVER_NAME ": failed to register " ++ "on platform\n"); ++ } ++ ++ return ret; ++} ++ ++static void __exit bcm_dmaman_drv_exit(void) ++{ ++ platform_driver_unregister(&bcm_dmaman_driver); ++} ++ ++module_init(bcm_dmaman_drv_init); ++module_exit(bcm_dmaman_drv_exit); ++ ++module_param(dmachans, int, 0644); ++ ++MODULE_AUTHOR("Gray Girling <grayg@broadcom.com>"); ++MODULE_DESCRIPTION("DMA channel manager driver"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_PARM_DESC(dmachans, "Bitmap of DMA channels available to the ARM"); +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/arm_control.h +@@ -0,0 +1,419 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/arm_control.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef __BCM2708_ARM_CONTROL_H ++#define __BCM2708_ARM_CONTROL_H ++ ++/* ++ * Definitions and addresses for the ARM CONTROL logic ++ * This file is manually generated. ++ */ ++ ++#define ARM_BASE 0x7E00B000 ++ ++/* Basic configuration */ ++#define ARM_CONTROL0 HW_REGISTER_RW(ARM_BASE+0x000) ++#define ARM_C0_SIZ128M 0x00000000 ++#define ARM_C0_SIZ256M 0x00000001 ++#define ARM_C0_SIZ512M 0x00000002 ++#define ARM_C0_SIZ1G 0x00000003 ++#define ARM_C0_BRESP0 0x00000000 ++#define ARM_C0_BRESP1 0x00000004 ++#define ARM_C0_BRESP2 0x00000008 ++#define ARM_C0_BOOTHI 0x00000010 ++#define ARM_C0_UNUSED05 0x00000020 /* free */ ++#define ARM_C0_FULLPERI 0x00000040 ++#define ARM_C0_UNUSED78 0x00000180 /* free */ ++#define ARM_C0_JTAGMASK 0x00000E00 ++#define ARM_C0_JTAGOFF 0x00000000 ++#define ARM_C0_JTAGBASH 0x00000800 /* Debug on GPIO off */ ++#define ARM_C0_JTAGGPIO 0x00000C00 /* Debug on GPIO on */ ++#define ARM_C0_APROTMSK 0x0000F000 ++#define ARM_C0_DBG0SYNC 0x00010000 /* VPU0 halt sync */ ++#define ARM_C0_DBG1SYNC 0x00020000 /* VPU1 halt sync */ ++#define ARM_C0_SWDBGREQ 0x00040000 /* HW debug request */ ++#define ARM_C0_PASSHALT 0x00080000 /* ARM halt passed to debugger */ ++#define ARM_C0_PRIO_PER 0x00F00000 /* per priority mask */ ++#define ARM_C0_PRIO_L2 0x0F000000 ++#define ARM_C0_PRIO_UC 0xF0000000 ++ ++#define ARM_C0_APROTPASS 0x0000A000 /* Translate 1:1 */ ++#define ARM_C0_APROTUSER 0x00000000 /* Only user mode */ ++#define ARM_C0_APROTSYST 0x0000F000 /* Only system mode */ ++ ++ ++#define ARM_CONTROL1 HW_REGISTER_RW(ARM_BASE+0x440) ++#define ARM_C1_TIMER 0x00000001 /* re-route timer IRQ to VC */ ++#define ARM_C1_MAIL 0x00000002 /* re-route Mail IRQ to VC */ ++#define ARM_C1_BELL0 0x00000004 /* re-route Doorbell 0 to VC */ ++#define ARM_C1_BELL1 0x00000008 /* re-route Doorbell 1 to VC */ ++#define ARM_C1_PERSON 0x00000100 /* peripherals on */ ++#define ARM_C1_REQSTOP 0x00000200 /* ASYNC bridge request stop */ ++ ++#define ARM_STATUS HW_REGISTER_RW(ARM_BASE+0x444) ++#define ARM_S_ACKSTOP 0x80000000 /* Bridge stopped */ ++#define ARM_S_READPEND 0x000003FF /* pending reads counter */ ++#define ARM_S_WRITPEND 0x000FFC00 /* pending writes counter */ ++ ++#define ARM_ERRHALT HW_REGISTER_RW(ARM_BASE+0x448) ++#define ARM_EH_PERIBURST 0x00000001 /* Burst write seen on peri bus */ ++#define ARM_EH_ILLADDRS1 0x00000002 /* Address bits 25-27 error */ ++#define ARM_EH_ILLADDRS2 0x00000004 /* Address bits 31-28 error */ ++#define ARM_EH_VPU0HALT 0x00000008 /* VPU0 halted & in debug mode */ ++#define ARM_EH_VPU1HALT 0x00000010 /* VPU1 halted & in debug mode */ ++#define ARM_EH_ARMHALT 0x00000020 /* ARM in halted debug mode */ ++ ++#define ARM_ID_SECURE HW_REGISTER_RW(ARM_BASE+0x00C) ++#define ARM_ID HW_REGISTER_RW(ARM_BASE+0x44C) ++#define ARM_IDVAL 0x364D5241 ++ ++/* Translation memory */ ++#define ARM_TRANSLATE HW_REGISTER_RW(ARM_BASE+0x100) ++/* 32 locations: 0x100.. 0x17F */ ++/* 32 spare means we CAN go to 64 pages.... */ ++ ++ ++/* Interrupts */ ++#define ARM_IRQ_PEND0 HW_REGISTER_RW(ARM_BASE+0x200) /* Top IRQ bits */ ++#define ARM_I0_TIMER 0x00000001 /* timer IRQ */ ++#define ARM_I0_MAIL 0x00000002 /* Mail IRQ */ ++#define ARM_I0_BELL0 0x00000004 /* Doorbell 0 */ ++#define ARM_I0_BELL1 0x00000008 /* Doorbell 1 */ ++#define ARM_I0_BANK1 0x00000100 /* Bank1 IRQ */ ++#define ARM_I0_BANK2 0x00000200 /* Bank2 IRQ */ ++ ++#define ARM_IRQ_PEND1 HW_REGISTER_RW(ARM_BASE+0x204) /* All bank1 IRQ bits */ ++/* todo: all I1_interrupt sources */ ++#define ARM_IRQ_PEND2 HW_REGISTER_RW(ARM_BASE+0x208) /* All bank2 IRQ bits */ ++/* todo: all I2_interrupt sources */ ++ ++#define ARM_IRQ_FAST HW_REGISTER_RW(ARM_BASE+0x20C) /* FIQ control */ ++#define ARM_IF_INDEX 0x0000007F /* FIQ select */ ++#define ARM_IF_ENABLE 0x00000080 /* FIQ enable */ ++#define ARM_IF_VCMASK 0x0000003F /* FIQ = (index from VC source) */ ++#define ARM_IF_TIMER 0x00000040 /* FIQ = ARM timer */ ++#define ARM_IF_MAIL 0x00000041 /* FIQ = ARM Mail */ ++#define ARM_IF_BELL0 0x00000042 /* FIQ = ARM Doorbell 0 */ ++#define ARM_IF_BELL1 0x00000043 /* FIQ = ARM Doorbell 1 */ ++#define ARM_IF_VP0HALT 0x00000044 /* FIQ = VPU0 Halt seen */ ++#define ARM_IF_VP1HALT 0x00000045 /* FIQ = VPU1 Halt seen */ ++#define ARM_IF_ILLEGAL 0x00000046 /* FIQ = Illegal access seen */ ++ ++#define ARM_IRQ_ENBL1 HW_REGISTER_RW(ARM_BASE+0x210) /* Bank1 enable bits */ ++#define ARM_IRQ_ENBL2 HW_REGISTER_RW(ARM_BASE+0x214) /* Bank2 enable bits */ ++#define ARM_IRQ_ENBL3 HW_REGISTER_RW(ARM_BASE+0x218) /* ARM irqs enable bits */ ++#define ARM_IRQ_DIBL1 HW_REGISTER_RW(ARM_BASE+0x21C) /* Bank1 disable bits */ ++#define ARM_IRQ_DIBL2 HW_REGISTER_RW(ARM_BASE+0x220) /* Bank2 disable bits */ ++#define ARM_IRQ_DIBL3 HW_REGISTER_RW(ARM_BASE+0x224) /* ARM irqs disable bits */ ++#define ARM_IE_TIMER 0x00000001 /* Timer IRQ */ ++#define ARM_IE_MAIL 0x00000002 /* Mail IRQ */ ++#define ARM_IE_BELL0 0x00000004 /* Doorbell 0 */ ++#define ARM_IE_BELL1 0x00000008 /* Doorbell 1 */ ++#define ARM_IE_VP0HALT 0x00000010 /* VPU0 Halt */ ++#define ARM_IE_VP1HALT 0x00000020 /* VPU1 Halt */ ++#define ARM_IE_ILLEGAL 0x00000040 /* Illegal access seen */ ++ ++/* Timer */ ++/* For reg. fields see sp804 spec. */ ++#define ARM_T_LOAD HW_REGISTER_RW(ARM_BASE+0x400) ++#define ARM_T_VALUE HW_REGISTER_RW(ARM_BASE+0x404) ++#define ARM_T_CONTROL HW_REGISTER_RW(ARM_BASE+0x408) ++#define ARM_T_IRQCNTL HW_REGISTER_RW(ARM_BASE+0x40C) ++#define ARM_T_RAWIRQ HW_REGISTER_RW(ARM_BASE+0x410) ++#define ARM_T_MSKIRQ HW_REGISTER_RW(ARM_BASE+0x414) ++#define ARM_T_RELOAD HW_REGISTER_RW(ARM_BASE+0x418) ++#define ARM_T_PREDIV HW_REGISTER_RW(ARM_BASE+0x41c) ++#define ARM_T_FREECNT HW_REGISTER_RW(ARM_BASE+0x420) ++ ++#define TIMER_CTRL_ONESHOT (1 << 0) ++#define TIMER_CTRL_32BIT (1 << 1) ++#define TIMER_CTRL_DIV1 (0 << 2) ++#define TIMER_CTRL_DIV16 (1 << 2) ++#define TIMER_CTRL_DIV256 (2 << 2) ++#define TIMER_CTRL_IE (1 << 5) ++#define TIMER_CTRL_PERIODIC (1 << 6) ++#define TIMER_CTRL_ENABLE (1 << 7) ++#define TIMER_CTRL_DBGHALT (1 << 8) ++#define TIMER_CTRL_ENAFREE (1 << 9) ++#define TIMER_CTRL_FREEDIV_SHIFT 16) ++#define TIMER_CTRL_FREEDIV_MASK 0xff ++ ++/* Semaphores, Doorbells, Mailboxes */ ++#define ARM_SBM_OWN0 (ARM_BASE+0x800) ++#define ARM_SBM_OWN1 (ARM_BASE+0x900) ++#define ARM_SBM_OWN2 (ARM_BASE+0xA00) ++#define ARM_SBM_OWN3 (ARM_BASE+0xB00) ++ ++/* MAILBOXES ++ * Register flags are common across all ++ * owner registers. See end of this section ++ * ++ * Semaphores, Doorbells, Mailboxes Owner 0 ++ * ++ */ ++ ++#define ARM_0_SEMS HW_REGISTER_RW(ARM_SBM_OWN0+0x00) ++#define ARM_0_SEM0 HW_REGISTER_RW(ARM_SBM_OWN0+0x00) ++#define ARM_0_SEM1 HW_REGISTER_RW(ARM_SBM_OWN0+0x04) ++#define ARM_0_SEM2 HW_REGISTER_RW(ARM_SBM_OWN0+0x08) ++#define ARM_0_SEM3 HW_REGISTER_RW(ARM_SBM_OWN0+0x0C) ++#define ARM_0_SEM4 HW_REGISTER_RW(ARM_SBM_OWN0+0x10) ++#define ARM_0_SEM5 HW_REGISTER_RW(ARM_SBM_OWN0+0x14) ++#define ARM_0_SEM6 HW_REGISTER_RW(ARM_SBM_OWN0+0x18) ++#define ARM_0_SEM7 HW_REGISTER_RW(ARM_SBM_OWN0+0x1C) ++#define ARM_0_BELL0 HW_REGISTER_RW(ARM_SBM_OWN0+0x40) ++#define ARM_0_BELL1 HW_REGISTER_RW(ARM_SBM_OWN0+0x44) ++#define ARM_0_BELL2 HW_REGISTER_RW(ARM_SBM_OWN0+0x48) ++#define ARM_0_BELL3 HW_REGISTER_RW(ARM_SBM_OWN0+0x4C) ++/* MAILBOX 0 access in Owner 0 area */ ++/* Some addresses should ONLY be used by owner 0 */ ++#define ARM_0_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN0+0x80) /* .. 0x8C (4 locations) */ ++#define ARM_0_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN0+0x80) /* .. 0x8C (4 locations) Normal read */ ++#define ARM_0_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN0+0x90) /* none-pop read */ ++#define ARM_0_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN0+0x94) /* Sender read (only LS 2 bits) */ ++#define ARM_0_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN0+0x98) /* Status read */ ++#define ARM_0_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN0+0x9C) /* Config read/write */ ++/* MAILBOX 1 access in Owner 0 area */ ++/* Owner 0 should only WRITE to this mailbox */ ++#define ARM_0_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN0+0xA0) /* .. 0xAC (4 locations) */ ++/*#define ARM_0_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN0+0xA0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_0_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN0+0xB0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_0_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN0+0xB4) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_0_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN0+0xB8) /* Status read */ ++/*#define ARM_0_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN0+0xBC) */ /* DO NOT USE THIS !!!!! */ ++/* General SEM, BELL, MAIL config/status */ ++#define ARM_0_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN0+0xE0) /* semaphore clear/debug register */ ++#define ARM_0_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN0+0xE4) /* Doorbells clear/debug register */ ++#define ARM_0_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN0+0xF8) /* ALL interrupts */ ++#define ARM_0_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN0+0xFC) /* IRQS pending for owner 0 */ ++ ++/* Semaphores, Doorbells, Mailboxes Owner 1 */ ++#define ARM_1_SEMS HW_REGISTER_RW(ARM_SBM_OWN1+0x00) ++#define ARM_1_SEM0 HW_REGISTER_RW(ARM_SBM_OWN1+0x00) ++#define ARM_1_SEM1 HW_REGISTER_RW(ARM_SBM_OWN1+0x04) ++#define ARM_1_SEM2 HW_REGISTER_RW(ARM_SBM_OWN1+0x08) ++#define ARM_1_SEM3 HW_REGISTER_RW(ARM_SBM_OWN1+0x0C) ++#define ARM_1_SEM4 HW_REGISTER_RW(ARM_SBM_OWN1+0x10) ++#define ARM_1_SEM5 HW_REGISTER_RW(ARM_SBM_OWN1+0x14) ++#define ARM_1_SEM6 HW_REGISTER_RW(ARM_SBM_OWN1+0x18) ++#define ARM_1_SEM7 HW_REGISTER_RW(ARM_SBM_OWN1+0x1C) ++#define ARM_1_BELL0 HW_REGISTER_RW(ARM_SBM_OWN1+0x40) ++#define ARM_1_BELL1 HW_REGISTER_RW(ARM_SBM_OWN1+0x44) ++#define ARM_1_BELL2 HW_REGISTER_RW(ARM_SBM_OWN1+0x48) ++#define ARM_1_BELL3 HW_REGISTER_RW(ARM_SBM_OWN1+0x4C) ++/* MAILBOX 0 access in Owner 0 area */ ++/* Owner 1 should only WRITE to this mailbox */ ++#define ARM_1_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN1+0x80) /* .. 0x8C (4 locations) */ ++/*#define ARM_1_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN1+0x80) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_1_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN1+0x90) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_1_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN1+0x94) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_1_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN1+0x98) /* Status read */ ++/*#define ARM_1_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN1+0x9C) */ /* DO NOT USE THIS !!!!! */ ++/* MAILBOX 1 access in Owner 0 area */ ++#define ARM_1_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN1+0xA0) /* .. 0xAC (4 locations) */ ++#define ARM_1_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN1+0xA0) /* .. 0xAC (4 locations) Normal read */ ++#define ARM_1_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN1+0xB0) /* none-pop read */ ++#define ARM_1_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN1+0xB4) /* Sender read (only LS 2 bits) */ ++#define ARM_1_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN1+0xB8) /* Status read */ ++#define ARM_1_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN1+0xBC) ++/* General SEM, BELL, MAIL config/status */ ++#define ARM_1_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN1+0xE0) /* semaphore clear/debug register */ ++#define ARM_1_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN1+0xE4) /* Doorbells clear/debug register */ ++#define ARM_1_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN1+0xFC) /* IRQS pending for owner 1 */ ++#define ARM_1_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN1+0xF8) /* ALL interrupts */ ++ ++/* Semaphores, Doorbells, Mailboxes Owner 2 */ ++#define ARM_2_SEMS HW_REGISTER_RW(ARM_SBM_OWN2+0x00) ++#define ARM_2_SEM0 HW_REGISTER_RW(ARM_SBM_OWN2+0x00) ++#define ARM_2_SEM1 HW_REGISTER_RW(ARM_SBM_OWN2+0x04) ++#define ARM_2_SEM2 HW_REGISTER_RW(ARM_SBM_OWN2+0x08) ++#define ARM_2_SEM3 HW_REGISTER_RW(ARM_SBM_OWN2+0x0C) ++#define ARM_2_SEM4 HW_REGISTER_RW(ARM_SBM_OWN2+0x10) ++#define ARM_2_SEM5 HW_REGISTER_RW(ARM_SBM_OWN2+0x14) ++#define ARM_2_SEM6 HW_REGISTER_RW(ARM_SBM_OWN2+0x18) ++#define ARM_2_SEM7 HW_REGISTER_RW(ARM_SBM_OWN2+0x1C) ++#define ARM_2_BELL0 HW_REGISTER_RW(ARM_SBM_OWN2+0x40) ++#define ARM_2_BELL1 HW_REGISTER_RW(ARM_SBM_OWN2+0x44) ++#define ARM_2_BELL2 HW_REGISTER_RW(ARM_SBM_OWN2+0x48) ++#define ARM_2_BELL3 HW_REGISTER_RW(ARM_SBM_OWN2+0x4C) ++/* MAILBOX 0 access in Owner 2 area */ ++/* Owner 2 should only WRITE to this mailbox */ ++#define ARM_2_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN2+0x80) /* .. 0x8C (4 locations) */ ++/*#define ARM_2_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN2+0x80) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_2_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN2+0x90) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_2_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN2+0x94) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_2_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN2+0x98) /* Status read */ ++/*#define ARM_2_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN2+0x9C) */ /* DO NOT USE THIS !!!!! */ ++/* MAILBOX 1 access in Owner 2 area */ ++/* Owner 2 should only WRITE to this mailbox */ ++#define ARM_2_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN2+0xA0) /* .. 0xAC (4 locations) */ ++/*#define ARM_2_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN2+0xA0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_2_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN2+0xB0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_2_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN2+0xB4) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_2_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN2+0xB8) /* Status read */ ++/*#define ARM_2_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN2+0xBC) */ /* DO NOT USE THIS !!!!! */ ++/* General SEM, BELL, MAIL config/status */ ++#define ARM_2_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN2+0xE0) /* semaphore clear/debug register */ ++#define ARM_2_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN2+0xE4) /* Doorbells clear/debug register */ ++#define ARM_2_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN2+0xFC) /* IRQS pending for owner 2 */ ++#define ARM_2_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN2+0xF8) /* ALL interrupts */ ++ ++/* Semaphores, Doorbells, Mailboxes Owner 3 */ ++#define ARM_3_SEMS HW_REGISTER_RW(ARM_SBM_OWN3+0x00) ++#define ARM_3_SEM0 HW_REGISTER_RW(ARM_SBM_OWN3+0x00) ++#define ARM_3_SEM1 HW_REGISTER_RW(ARM_SBM_OWN3+0x04) ++#define ARM_3_SEM2 HW_REGISTER_RW(ARM_SBM_OWN3+0x08) ++#define ARM_3_SEM3 HW_REGISTER_RW(ARM_SBM_OWN3+0x0C) ++#define ARM_3_SEM4 HW_REGISTER_RW(ARM_SBM_OWN3+0x10) ++#define ARM_3_SEM5 HW_REGISTER_RW(ARM_SBM_OWN3+0x14) ++#define ARM_3_SEM6 HW_REGISTER_RW(ARM_SBM_OWN3+0x18) ++#define ARM_3_SEM7 HW_REGISTER_RW(ARM_SBM_OWN3+0x1C) ++#define ARM_3_BELL0 HW_REGISTER_RW(ARM_SBM_OWN3+0x40) ++#define ARM_3_BELL1 HW_REGISTER_RW(ARM_SBM_OWN3+0x44) ++#define ARM_3_BELL2 HW_REGISTER_RW(ARM_SBM_OWN3+0x48) ++#define ARM_3_BELL3 HW_REGISTER_RW(ARM_SBM_OWN3+0x4C) ++/* MAILBOX 0 access in Owner 3 area */ ++/* Owner 3 should only WRITE to this mailbox */ ++#define ARM_3_MAIL0_WRT HW_REGISTER_RW(ARM_SBM_OWN3+0x80) /* .. 0x8C (4 locations) */ ++/*#define ARM_3_MAIL0_RD HW_REGISTER_RW(ARM_SBM_OWN3+0x80) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_3_MAIL0_POL HW_REGISTER_RW(ARM_SBM_OWN3+0x90) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_3_MAIL0_SND HW_REGISTER_RW(ARM_SBM_OWN3+0x94) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_3_MAIL0_STA HW_REGISTER_RW(ARM_SBM_OWN3+0x98) /* Status read */ ++/*#define ARM_3_MAIL0_CNF HW_REGISTER_RW(ARM_SBM_OWN3+0x9C) */ /* DO NOT USE THIS !!!!! */ ++/* MAILBOX 1 access in Owner 3 area */ ++/* Owner 3 should only WRITE to this mailbox */ ++#define ARM_3_MAIL1_WRT HW_REGISTER_RW(ARM_SBM_OWN3+0xA0) /* .. 0xAC (4 locations) */ ++/*#define ARM_3_MAIL1_RD HW_REGISTER_RW(ARM_SBM_OWN3+0xA0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_3_MAIL1_POL HW_REGISTER_RW(ARM_SBM_OWN3+0xB0) */ /* DO NOT USE THIS !!!!! */ ++/*#define ARM_3_MAIL1_SND HW_REGISTER_RW(ARM_SBM_OWN3+0xB4) */ /* DO NOT USE THIS !!!!! */ ++#define ARM_3_MAIL1_STA HW_REGISTER_RW(ARM_SBM_OWN3+0xB8) /* Status read */ ++/*#define ARM_3_MAIL1_CNF HW_REGISTER_RW(ARM_SBM_OWN3+0xBC) */ /* DO NOT USE THIS !!!!! */ ++/* General SEM, BELL, MAIL config/status */ ++#define ARM_3_SEMCLRDBG HW_REGISTER_RW(ARM_SBM_OWN3+0xE0) /* semaphore clear/debug register */ ++#define ARM_3_BELLCLRDBG HW_REGISTER_RW(ARM_SBM_OWN3+0xE4) /* Doorbells clear/debug register */ ++#define ARM_3_MY_IRQS HW_REGISTER_RW(ARM_SBM_OWN3+0xFC) /* IRQS pending for owner 3 */ ++#define ARM_3_ALL_IRQS HW_REGISTER_RW(ARM_SBM_OWN3+0xF8) /* ALL interrupts */ ++ ++ ++ ++/* Mailbox flags. Valid for all owners */ ++ ++/* Mailbox status register (...0x98) */ ++#define ARM_MS_FULL 0x80000000 ++#define ARM_MS_EMPTY 0x40000000 ++#define ARM_MS_LEVEL 0x400000FF /* Max. value depdnds on mailbox depth parameter */ ++ ++/* MAILBOX config/status register (...0x9C) */ ++/* ANY write to this register clears the error bits! */ ++#define ARM_MC_IHAVEDATAIRQEN 0x00000001 /* mailbox irq enable: has data */ ++#define ARM_MC_IHAVESPACEIRQEN 0x00000002 /* mailbox irq enable: has space */ ++#define ARM_MC_OPPISEMPTYIRQEN 0x00000004 /* mailbox irq enable: Opp. is empty */ ++#define ARM_MC_MAIL_CLEAR 0x00000008 /* mailbox clear write 1, then 0 */ ++#define ARM_MC_IHAVEDATAIRQPEND 0x00000010 /* mailbox irq pending: has space */ ++#define ARM_MC_IHAVESPACEIRQPEND 0x00000020 /* mailbox irq pending: Opp. is empty */ ++#define ARM_MC_OPPISEMPTYIRQPEND 0x00000040 /* mailbox irq pending */ ++/* Bit 7 is unused */ ++#define ARM_MC_ERRNOOWN 0x00000100 /* error : none owner read from mailbox */ ++#define ARM_MC_ERROVERFLW 0x00000200 /* error : write to fill mailbox */ ++#define ARM_MC_ERRUNDRFLW 0x00000400 /* error : read from empty mailbox */ ++ ++/* Semaphore clear/debug register (...0xE0) */ ++#define ARM_SD_OWN0 0x00000003 /* Owner of sem 0 */ ++#define ARM_SD_OWN1 0x0000000C /* Owner of sem 1 */ ++#define ARM_SD_OWN2 0x00000030 /* Owner of sem 2 */ ++#define ARM_SD_OWN3 0x000000C0 /* Owner of sem 3 */ ++#define ARM_SD_OWN4 0x00000300 /* Owner of sem 4 */ ++#define ARM_SD_OWN5 0x00000C00 /* Owner of sem 5 */ ++#define ARM_SD_OWN6 0x00003000 /* Owner of sem 6 */ ++#define ARM_SD_OWN7 0x0000C000 /* Owner of sem 7 */ ++#define ARM_SD_SEM0 0x00010000 /* Status of sem 0 */ ++#define ARM_SD_SEM1 0x00020000 /* Status of sem 1 */ ++#define ARM_SD_SEM2 0x00040000 /* Status of sem 2 */ ++#define ARM_SD_SEM3 0x00080000 /* Status of sem 3 */ ++#define ARM_SD_SEM4 0x00100000 /* Status of sem 4 */ ++#define ARM_SD_SEM5 0x00200000 /* Status of sem 5 */ ++#define ARM_SD_SEM6 0x00400000 /* Status of sem 6 */ ++#define ARM_SD_SEM7 0x00800000 /* Status of sem 7 */ ++ ++/* Doorbells clear/debug register (...0xE4) */ ++#define ARM_BD_OWN0 0x00000003 /* Owner of doorbell 0 */ ++#define ARM_BD_OWN1 0x0000000C /* Owner of doorbell 1 */ ++#define ARM_BD_OWN2 0x00000030 /* Owner of doorbell 2 */ ++#define ARM_BD_OWN3 0x000000C0 /* Owner of doorbell 3 */ ++#define ARM_BD_BELL0 0x00000100 /* Status of doorbell 0 */ ++#define ARM_BD_BELL1 0x00000200 /* Status of doorbell 1 */ ++#define ARM_BD_BELL2 0x00000400 /* Status of doorbell 2 */ ++#define ARM_BD_BELL3 0x00000800 /* Status of doorbell 3 */ ++ ++/* MY IRQS register (...0xF8) */ ++#define ARM_MYIRQ_BELL 0x00000001 /* This owner has a doorbell IRQ */ ++#define ARM_MYIRQ_MAIL 0x00000002 /* This owner has a mailbox IRQ */ ++ ++/* ALL IRQS register (...0xF8) */ ++#define ARM_AIS_BELL0 0x00000001 /* Doorbell 0 IRQ pending */ ++#define ARM_AIS_BELL1 0x00000002 /* Doorbell 1 IRQ pending */ ++#define ARM_AIS_BELL2 0x00000004 /* Doorbell 2 IRQ pending */ ++#define ARM_AIS_BELL3 0x00000008 /* Doorbell 3 IRQ pending */ ++#define ARM_AIS0_HAVEDATA 0x00000010 /* MAIL 0 has data IRQ pending */ ++#define ARM_AIS0_HAVESPAC 0x00000020 /* MAIL 0 has space IRQ pending */ ++#define ARM_AIS0_OPPEMPTY 0x00000040 /* MAIL 0 opposite is empty IRQ */ ++#define ARM_AIS1_HAVEDATA 0x00000080 /* MAIL 1 has data IRQ pending */ ++#define ARM_AIS1_HAVESPAC 0x00000100 /* MAIL 1 has space IRQ pending */ ++#define ARM_AIS1_OPPEMPTY 0x00000200 /* MAIL 1 opposite is empty IRQ */ ++/* Note that bell-0, bell-1 and MAIL0 IRQ go only to the ARM */ ++/* Whilst that bell-2, bell-3 and MAIL1 IRQ go only to the VC */ ++/* */ ++/* ARM JTAG BASH */ ++/* */ ++#define AJB_BASE 0x7e2000c0 ++ ++#define AJBCONF HW_REGISTER_RW(AJB_BASE+0x00) ++#define AJB_BITS0 0x000000 ++#define AJB_BITS4 0x000004 ++#define AJB_BITS8 0x000008 ++#define AJB_BITS12 0x00000C ++#define AJB_BITS16 0x000010 ++#define AJB_BITS20 0x000014 ++#define AJB_BITS24 0x000018 ++#define AJB_BITS28 0x00001C ++#define AJB_BITS32 0x000020 ++#define AJB_BITS34 0x000022 ++#define AJB_OUT_MS 0x000040 ++#define AJB_OUT_LS 0x000000 ++#define AJB_INV_CLK 0x000080 ++#define AJB_D0_RISE 0x000100 ++#define AJB_D0_FALL 0x000000 ++#define AJB_D1_RISE 0x000200 ++#define AJB_D1_FALL 0x000000 ++#define AJB_IN_RISE 0x000400 ++#define AJB_IN_FALL 0x000000 ++#define AJB_ENABLE 0x000800 ++#define AJB_HOLD0 0x000000 ++#define AJB_HOLD1 0x001000 ++#define AJB_HOLD2 0x002000 ++#define AJB_HOLD3 0x003000 ++#define AJB_RESETN 0x004000 ++#define AJB_CLKSHFT 16 ++#define AJB_BUSY 0x80000000 ++#define AJBTMS HW_REGISTER_RW(AJB_BASE+0x04) ++#define AJBTDI HW_REGISTER_RW(AJB_BASE+0x08) ++#define AJBTDO HW_REGISTER_RW(AJB_BASE+0x0c) ++ ++#endif +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/arm_power.h +@@ -0,0 +1,60 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/include/mach/arm_power.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _ARM_POWER_H ++#define _ARM_POWER_H ++ ++/* Use meaningful names on each side */ ++#ifdef __VIDEOCORE__ ++#define PREFIX(x) ARM_##x ++#else ++#define PREFIX(x) BCM_##x ++#endif ++ ++enum { ++ PREFIX(POWER_SDCARD_BIT), ++ PREFIX(POWER_UART_BIT), ++ PREFIX(POWER_MINIUART_BIT), ++ PREFIX(POWER_USB_BIT), ++ PREFIX(POWER_I2C0_BIT), ++ PREFIX(POWER_I2C1_BIT), ++ PREFIX(POWER_I2C2_BIT), ++ PREFIX(POWER_SPI_BIT), ++ PREFIX(POWER_CCP2TX_BIT), ++ ++ PREFIX(POWER_MAX) ++}; ++ ++enum { ++ PREFIX(POWER_SDCARD) = (1 << PREFIX(POWER_SDCARD_BIT)), ++ PREFIX(POWER_UART) = (1 << PREFIX(POWER_UART_BIT)), ++ PREFIX(POWER_MINIUART) = (1 << PREFIX(POWER_MINIUART_BIT)), ++ PREFIX(POWER_USB) = (1 << PREFIX(POWER_USB_BIT)), ++ PREFIX(POWER_I2C0) = (1 << PREFIX(POWER_I2C0_BIT)), ++ PREFIX(POWER_I2C1_MASK) = (1 << PREFIX(POWER_I2C1_BIT)), ++ PREFIX(POWER_I2C2_MASK) = (1 << PREFIX(POWER_I2C2_BIT)), ++ PREFIX(POWER_SPI_MASK) = (1 << PREFIX(POWER_SPI_BIT)), ++ PREFIX(POWER_CCP2TX_MASK) = (1 << PREFIX(POWER_CCP2TX_BIT)), ++ ++ PREFIX(POWER_MASK) = (1 << PREFIX(POWER_MAX)) - 1, ++ PREFIX(POWER_NONE) = 0 ++}; ++ ++#endif +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/clkdev.h +@@ -0,0 +1,7 @@ ++#ifndef __ASM_MACH_CLKDEV_H ++#define __ASM_MACH_CLKDEV_H ++ ++#define __clk_get(clk) ({ 1; }) ++#define __clk_put(clk) do { } while (0) ++ ++#endif +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/debug-macro.S +@@ -0,0 +1,24 @@ ++/* arch/arm/mach-bcm2708/include/mach/debug-macro.S ++ * ++ * Debugging macro include header ++ * ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 1994-1999 Russell King ++ * Moved from linux/arch/arm/kernel/debug.S by Ben Dooks ++ * ++ * 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. ++ * ++*/ ++ ++ .macro addruart, rx, tmp ++ mrc p15, 0, \rx, c1, c0 ++ tst \rx, #1 @ MMU enabled? ++ moveq \rx, #0x08000000 ++ movne \rx, #0xf8000000 @ virtual base ++ orr \rx, \rx, #0x00200000 ++ orr \rx, \rx, #0x00001000 ++ .endm ++ ++#include <asm/hardware/debug-pl01x.S> +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/dma.h +@@ -0,0 +1,84 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/include/mach/dma.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++ ++#ifndef _MACH_BCM2708_DMA_H ++#define _MACH_BCM2708_DMA_H ++ ++#define BCM_DMAMAN_DRIVER_NAME "bcm2708_dma" ++ ++/* DMA CS Control and Status bits */ ++#define BCM2708_DMA_ACTIVE (1 << 0) ++#define BCM2708_DMA_INT (1 << 2) ++#define BCM2708_DMA_ISPAUSED (1 << 4) /* Pause requested or not active */ ++#define BCM2708_DMA_ISHELD (1 << 5) /* Is held by DREQ flow control */ ++#define BCM2708_DMA_ERR (1 << 8) ++#define BCM2708_DMA_ABORT (1 << 30) /* stop current CB, go to next, WO */ ++#define BCM2708_DMA_RESET (1 << 31) /* WO, self clearing */ ++ ++/* DMA control block "info" field bits */ ++#define BCM2708_DMA_INT_EN (1 << 0) ++#define BCM2708_DMA_TDMODE (1 << 1) ++#define BCM2708_DMA_WAIT_RESP (1 << 3) ++#define BCM2708_DMA_D_INC (1 << 4) ++#define BCM2708_DMA_D_WIDTH (1 << 5) ++#define BCM2708_DMA_D_DREQ (1 << 6) ++#define BCM2708_DMA_S_INC (1 << 8) ++#define BCM2708_DMA_S_WIDTH (1 << 9) ++#define BCM2708_DMA_S_DREQ (1 << 10) ++ ++#define BCM2708_DMA_BURST(x) (((x)&0xf) << 12) ++#define BCM2708_DMA_PER_MAP(x) ((x) << 16) ++#define BCM2708_DMA_WAITS(x) (((x)&0x1f) << 21) ++ ++#define BCM2708_DMA_DREQ_EMMC 11 ++#define BCM2708_DMA_DREQ_SDHOST 13 ++ ++#define BCM2708_DMA_CS 0x00 /* Control and Status */ ++#define BCM2708_DMA_ADDR 0x04 ++/* the current control block appears in the following registers - read only */ ++#define BCM2708_DMA_INFO 0x08 ++#define BCM2708_DMA_NEXTCB 0x1C ++#define BCM2708_DMA_DEBUG 0x20 ++ ++#define BCM2708_DMA4_CS (BCM2708_DMA_CHAN(4)+BCM2708_DMA_CS) ++#define BCM2708_DMA4_ADDR (BCM2708_DMA_CHAN(4)+BCM2708_DMA_ADDR) ++ ++#define BCM2708_DMA_TDMODE_LEN(w, h) ((h) << 16 | (w)) ++ ++struct bcm2708_dma_cb { ++ unsigned long info; ++ unsigned long src; ++ unsigned long dst; ++ unsigned long length; ++ unsigned long stride; ++ unsigned long next; ++ unsigned long pad[2]; ++}; ++ ++extern int bcm_sg_suitable_for_dma(struct scatterlist *sg_ptr, int sg_len); ++extern void bcm_dma_start(void __iomem *dma_chan_base, ++ dma_addr_t control_block); ++extern void bcm_dma_wait_idle(void __iomem *dma_chan_base); ++extern int /*rc*/ bcm_dma_abort(void __iomem *dma_chan_base); ++ ++/* When listing features we can ask for when allocating DMA channels give ++ those with higher priority smaller ordinal numbers */ ++#define BCM_DMA_FEATURE_FAST_ORD 0 ++#define BCM_DMA_FEATURE_FAST (1<<BCM_DMA_FEATURE_FAST_ORD) ++#define BCM_DMA_FEATURE_COUNT 1 ++ ++/* return channel no or -ve error */ ++extern int bcm_dma_chan_alloc(unsigned preferred_feature_set, ++ void __iomem **out_dma_base, int *out_dma_irq); ++extern int bcm_dma_chan_free(int channel); ++ ++ ++#endif /* _MACH_BCM2708_DMA_H */ +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/entry-macro.S +@@ -0,0 +1,69 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/entry-macro.S ++ * ++ * Low-level IRQ helper macros for BCM2708 platforms ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#include <mach/hardware.h> ++ ++ .macro disable_fiq ++ .endm ++ ++ .macro get_irqnr_preamble, base, tmp ++ ldr \base, =IO_ADDRESS(ARMCTRL_IC_BASE) ++ .endm ++ ++ .macro arch_ret_to_user, tmp1, tmp2 ++ .endm ++ ++ .macro get_irqnr_and_base, irqnr, irqstat, base, tmp ++ /* get masked status */ ++ ldr \irqstat, [\base, #(ARM_IRQ_PEND0 - ARMCTRL_IC_BASE)] ++ mov \irqnr, #(ARM_IRQ0_BASE + 31) ++ and \tmp, \irqstat, #0x300 @ save bits 8 and 9 ++ /* clear bits 8 and 9, and test */ ++ bics \irqstat, \irqstat, #0x300 ++ bne 1010f ++ ++ tst \tmp, #0x100 ++ ldrne \irqstat, [\base, #(ARM_IRQ_PEND1 - ARMCTRL_IC_BASE)] ++ movne \irqnr, #(ARM_IRQ1_BASE + 31) ++ @ Mask out the interrupts also present in PEND0 - see SW-5809 ++ bicne \irqstat, #((1<<7) | (1<<9) | (1<<10)) ++ bicne \irqstat, #((1<<18) | (1<<19)) ++ bne 1010f ++ ++ tst \tmp, #0x200 ++ ldrne \irqstat, [\base, #(ARM_IRQ_PEND2 - ARMCTRL_IC_BASE)] ++ movne \irqnr, #(ARM_IRQ2_BASE + 31) ++ @ Mask out the interrupts also present in PEND0 - see SW-5809 ++ bicne \irqstat, #((1<<21) | (1<<22) | (1<<23) | (1<<24) | (1<<25)) ++ bicne \irqstat, #((1<<30)) ++ beq 1020f ++ ++1010: ++ @ For non-zero x, LSB(x) = 31 - CLZ(x^(x-1)) ++ @ N.B. CLZ is an ARM5 instruction. ++ sub \tmp, \irqstat, #1 ++ eor \irqstat, \irqstat, \tmp ++ clz \tmp, \irqstat ++ sub \irqnr, \tmp ++ ++1020: @ EQ will be set if no irqs pending ++ ++ .endm +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/frc.h +@@ -0,0 +1,38 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/timex.h ++ * ++ * BCM2708 free running counter (timer) ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _MACH_FRC_H ++#define _MACH_FRC_H ++ ++#define FRC_TICK_RATE (1000000) ++ ++/*! Free running counter incrementing at the CLOCK_TICK_RATE ++ (slightly faster than frc_clock_ticks63() ++ */ ++extern unsigned long frc_clock_ticks32(void); ++ ++/*! Free running counter incrementing at the CLOCK_TICK_RATE ++ * Note - top bit should be ignored (see cnt32_to_63) ++ */ ++extern unsigned long long frc_clock_ticks63(void); ++ ++#endif +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/gpio.h +@@ -0,0 +1,48 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/gpio.h ++ * ++ * This file is licensed under the terms of the GNU General Public ++ * License version 2. This program is licensed "as is" without any ++ * warranty of any kind, whether express or implied. ++ */ ++ ++#ifndef __ASM_ARCH_GPIO_H ++#define __ASM_ARCH_GPIO_H ++ ++#define ARCH_NR_GPIOS 54 // number of gpio lines ++ ++#include <asm-generic/gpio.h> ++ ++ ++#ifdef CONFIG_GPIOLIB ++ ++static inline int gpio_get_value(unsigned gpio) ++{ ++ return __gpio_get_value(gpio); ++} ++ ++static inline void gpio_set_value(unsigned gpio, int value) ++{ ++ __gpio_set_value(gpio, value); ++} ++ ++static inline int gpio_cansleep(unsigned gpio) ++{ ++ return __gpio_cansleep(gpio); ++} ++ ++static inline int gpio_to_irq(unsigned gpio) ++{ ++ WARN_ON(1); ++ return -ENOSYS; ++} ++ ++static inline int irq_to_gpio(unsigned int irq) ++{ ++ WARN_ON(1); ++ return -EINVAL; ++} ++ ++#endif /* CONFIG_GPIOLIB */ ++ ++#endif +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/hardware.h +@@ -0,0 +1,28 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/hardware.h ++ * ++ * This file contains the hardware definitions of the BCM2708 devices. ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __ASM_ARCH_HARDWARE_H ++#define __ASM_ARCH_HARDWARE_H ++ ++#include <asm/sizes.h> ++#include <mach/platform.h> ++ ++#endif +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/io.h +@@ -0,0 +1,28 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/io.h ++ * ++ * Copyright (C) 2003 ARM Limited ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __ASM_ARM_ARCH_IO_H ++#define __ASM_ARM_ARCH_IO_H ++ ++#define IO_SPACE_LIMIT 0xffffffff ++ ++#define __io(a) __typesafe_io(a) ++#define __mem_pci(a) (a) ++ ++#endif +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/irqs.h +@@ -0,0 +1,190 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/irqs.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2003 ARM Limited ++ * Copyright (C) 2000 Deep Blue Solutions Ltd. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _BCM2708_IRQS_H_ ++#define _BCM2708_IRQS_H_ ++ ++#include <mach/platform.h> ++ ++/* ++ * IRQ interrupts definitions are the same as the INT definitions ++ * held within platform.h ++ */ ++#define IRQ_ARMCTRL_START 0 ++#define IRQ_TIMER0 (IRQ_ARMCTRL_START + INTERRUPT_TIMER0) ++#define IRQ_TIMER1 (IRQ_ARMCTRL_START + INTERRUPT_TIMER1) ++#define IRQ_TIMER2 (IRQ_ARMCTRL_START + INTERRUPT_TIMER2) ++#define IRQ_TIMER3 (IRQ_ARMCTRL_START + INTERRUPT_TIMER3) ++#define IRQ_CODEC0 (IRQ_ARMCTRL_START + INTERRUPT_CODEC0) ++#define IRQ_CODEC1 (IRQ_ARMCTRL_START + INTERRUPT_CODEC1) ++#define IRQ_CODEC2 (IRQ_ARMCTRL_START + INTERRUPT_CODEC2) ++#define IRQ_JPEG (IRQ_ARMCTRL_START + INTERRUPT_JPEG) ++#define IRQ_ISP (IRQ_ARMCTRL_START + INTERRUPT_ISP) ++#define IRQ_USB (IRQ_ARMCTRL_START + INTERRUPT_USB) ++#define IRQ_3D (IRQ_ARMCTRL_START + INTERRUPT_3D) ++#define IRQ_TRANSPOSER (IRQ_ARMCTRL_START + INTERRUPT_TRANSPOSER) ++#define IRQ_MULTICORESYNC0 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC0) ++#define IRQ_MULTICORESYNC1 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC1) ++#define IRQ_MULTICORESYNC2 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC2) ++#define IRQ_MULTICORESYNC3 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC3) ++#define IRQ_DMA0 (IRQ_ARMCTRL_START + INTERRUPT_DMA0) ++#define IRQ_DMA1 (IRQ_ARMCTRL_START + INTERRUPT_DMA1) ++#define IRQ_DMA2 (IRQ_ARMCTRL_START + INTERRUPT_DMA2) ++#define IRQ_DMA3 (IRQ_ARMCTRL_START + INTERRUPT_DMA3) ++#define IRQ_DMA4 (IRQ_ARMCTRL_START + INTERRUPT_DMA4) ++#define IRQ_DMA5 (IRQ_ARMCTRL_START + INTERRUPT_DMA5) ++#define IRQ_DMA6 (IRQ_ARMCTRL_START + INTERRUPT_DMA6) ++#define IRQ_DMA7 (IRQ_ARMCTRL_START + INTERRUPT_DMA7) ++#define IRQ_DMA8 (IRQ_ARMCTRL_START + INTERRUPT_DMA8) ++#define IRQ_DMA9 (IRQ_ARMCTRL_START + INTERRUPT_DMA9) ++#define IRQ_DMA10 (IRQ_ARMCTRL_START + INTERRUPT_DMA10) ++#define IRQ_DMA11 (IRQ_ARMCTRL_START + INTERRUPT_DMA11) ++#define IRQ_DMA12 (IRQ_ARMCTRL_START + INTERRUPT_DMA12) ++#define IRQ_AUX (IRQ_ARMCTRL_START + INTERRUPT_AUX) ++#define IRQ_ARM (IRQ_ARMCTRL_START + INTERRUPT_ARM) ++#define IRQ_VPUDMA (IRQ_ARMCTRL_START + INTERRUPT_VPUDMA) ++#define IRQ_HOSTPORT (IRQ_ARMCTRL_START + INTERRUPT_HOSTPORT) ++#define IRQ_VIDEOSCALER (IRQ_ARMCTRL_START + INTERRUPT_VIDEOSCALER) ++#define IRQ_CCP2TX (IRQ_ARMCTRL_START + INTERRUPT_CCP2TX) ++#define IRQ_SDC (IRQ_ARMCTRL_START + INTERRUPT_SDC) ++#define IRQ_DSI0 (IRQ_ARMCTRL_START + INTERRUPT_DSI0) ++#define IRQ_AVE (IRQ_ARMCTRL_START + INTERRUPT_AVE) ++#define IRQ_CAM0 (IRQ_ARMCTRL_START + INTERRUPT_CAM0) ++#define IRQ_CAM1 (IRQ_ARMCTRL_START + INTERRUPT_CAM1) ++#define IRQ_HDMI0 (IRQ_ARMCTRL_START + INTERRUPT_HDMI0) ++#define IRQ_HDMI1 (IRQ_ARMCTRL_START + INTERRUPT_HDMI1) ++#define IRQ_PIXELVALVE1 (IRQ_ARMCTRL_START + INTERRUPT_PIXELVALVE1) ++#define IRQ_I2CSPISLV (IRQ_ARMCTRL_START + INTERRUPT_I2CSPISLV) ++#define IRQ_DSI1 (IRQ_ARMCTRL_START + INTERRUPT_DSI1) ++#define IRQ_PWA0 (IRQ_ARMCTRL_START + INTERRUPT_PWA0) ++#define IRQ_PWA1 (IRQ_ARMCTRL_START + INTERRUPT_PWA1) ++#define IRQ_CPR (IRQ_ARMCTRL_START + INTERRUPT_CPR) ++#define IRQ_SMI (IRQ_ARMCTRL_START + INTERRUPT_SMI) ++#define IRQ_GPIO0 (IRQ_ARMCTRL_START + INTERRUPT_GPIO0) ++#define IRQ_GPIO1 (IRQ_ARMCTRL_START + INTERRUPT_GPIO1) ++#define IRQ_GPIO2 (IRQ_ARMCTRL_START + INTERRUPT_GPIO2) ++#define IRQ_GPIO3 (IRQ_ARMCTRL_START + INTERRUPT_GPIO3) ++#define IRQ_I2C (IRQ_ARMCTRL_START + INTERRUPT_I2C) ++#define IRQ_SPI (IRQ_ARMCTRL_START + INTERRUPT_SPI) ++#define IRQ_I2SPCM (IRQ_ARMCTRL_START + INTERRUPT_I2SPCM) ++#define IRQ_SDIO (IRQ_ARMCTRL_START + INTERRUPT_SDIO) ++#define IRQ_UART (IRQ_ARMCTRL_START + INTERRUPT_UART) ++#define IRQ_SLIMBUS (IRQ_ARMCTRL_START + INTERRUPT_SLIMBUS) ++#define IRQ_VEC (IRQ_ARMCTRL_START + INTERRUPT_VEC) ++#define IRQ_CPG (IRQ_ARMCTRL_START + INTERRUPT_CPG) ++#define IRQ_RNG (IRQ_ARMCTRL_START + INTERRUPT_RNG) ++#define IRQ_ARASANSDIO (IRQ_ARMCTRL_START + INTERRUPT_ARASANSDIO) ++#define IRQ_AVSPMON (IRQ_ARMCTRL_START + INTERRUPT_AVSPMON) ++ ++#define IRQ_ARM_TIMER (IRQ_ARMCTRL_START + INTERRUPT_ARM_TIMER) ++#define IRQ_ARM_MAILBOX (IRQ_ARMCTRL_START + INTERRUPT_ARM_MAILBOX) ++#define IRQ_ARM_DOORBELL_0 (IRQ_ARMCTRL_START + INTERRUPT_ARM_DOORBELL_0) ++#define IRQ_ARM_DOORBELL_1 (IRQ_ARMCTRL_START + INTERRUPT_ARM_DOORBELL_1) ++#define IRQ_VPU0_HALTED (IRQ_ARMCTRL_START + INTERRUPT_VPU0_HALTED) ++#define IRQ_VPU1_HALTED (IRQ_ARMCTRL_START + INTERRUPT_VPU1_HALTED) ++#define IRQ_ILLEGAL_TYPE0 (IRQ_ARMCTRL_START + INTERRUPT_ILLEGAL_TYPE0) ++#define IRQ_ILLEGAL_TYPE1 (IRQ_ARMCTRL_START + INTERRUPT_ILLEGAL_TYPE1) ++#define IRQ_PENDING1 (IRQ_ARMCTRL_START + INTERRUPT_PENDING1) ++#define IRQ_PENDING2 (IRQ_ARMCTRL_START + INTERRUPT_PENDING2) ++ ++/* ++ * FIQ interrupts definitions are the same as the INT definitions. ++ */ ++#define FIQ_TIMER0 INT_TIMER0 ++#define FIQ_TIMER1 INT_TIMER1 ++#define FIQ_TIMER2 INT_TIMER2 ++#define FIQ_TIMER3 INT_TIMER3 ++#define FIQ_CODEC0 INT_CODEC0 ++#define FIQ_CODEC1 INT_CODEC1 ++#define FIQ_CODEC2 INT_CODEC2 ++#define FIQ_JPEG INT_JPEG ++#define FIQ_ISP INT_ISP ++#define FIQ_USB INT_USB ++#define FIQ_3D INT_3D ++#define FIQ_TRANSPOSER INT_TRANSPOSER ++#define FIQ_MULTICORESYNC0 INT_MULTICORESYNC0 ++#define FIQ_MULTICORESYNC1 INT_MULTICORESYNC1 ++#define FIQ_MULTICORESYNC2 INT_MULTICORESYNC2 ++#define FIQ_MULTICORESYNC3 INT_MULTICORESYNC3 ++#define FIQ_DMA0 INT_DMA0 ++#define FIQ_DMA1 INT_DMA1 ++#define FIQ_DMA2 INT_DMA2 ++#define FIQ_DMA3 INT_DMA3 ++#define FIQ_DMA4 INT_DMA4 ++#define FIQ_DMA5 INT_DMA5 ++#define FIQ_DMA6 INT_DMA6 ++#define FIQ_DMA7 INT_DMA7 ++#define FIQ_DMA8 INT_DMA8 ++#define FIQ_DMA9 INT_DMA9 ++#define FIQ_DMA10 INT_DMA10 ++#define FIQ_DMA11 INT_DMA11 ++#define FIQ_DMA12 INT_DMA12 ++#define FIQ_AUX INT_AUX ++#define FIQ_ARM INT_ARM ++#define FIQ_VPUDMA INT_VPUDMA ++#define FIQ_HOSTPORT INT_HOSTPORT ++#define FIQ_VIDEOSCALER INT_VIDEOSCALER ++#define FIQ_CCP2TX INT_CCP2TX ++#define FIQ_SDC INT_SDC ++#define FIQ_DSI0 INT_DSI0 ++#define FIQ_AVE INT_AVE ++#define FIQ_CAM0 INT_CAM0 ++#define FIQ_CAM1 INT_CAM1 ++#define FIQ_HDMI0 INT_HDMI0 ++#define FIQ_HDMI1 INT_HDMI1 ++#define FIQ_PIXELVALVE1 INT_PIXELVALVE1 ++#define FIQ_I2CSPISLV INT_I2CSPISLV ++#define FIQ_DSI1 INT_DSI1 ++#define FIQ_PWA0 INT_PWA0 ++#define FIQ_PWA1 INT_PWA1 ++#define FIQ_CPR INT_CPR ++#define FIQ_SMI INT_SMI ++#define FIQ_GPIO0 INT_GPIO0 ++#define FIQ_GPIO1 INT_GPIO1 ++#define FIQ_GPIO2 INT_GPIO2 ++#define FIQ_GPIO3 INT_GPIO3 ++#define FIQ_I2C INT_I2C ++#define FIQ_SPI INT_SPI ++#define FIQ_I2SPCM INT_I2SPCM ++#define FIQ_SDIO INT_SDIO ++#define FIQ_UART INT_UART ++#define FIQ_SLIMBUS INT_SLIMBUS ++#define FIQ_VEC INT_VEC ++#define FIQ_CPG INT_CPG ++#define FIQ_RNG INT_RNG ++#define FIQ_ARASANSDIO INT_ARASANSDIO ++#define FIQ_AVSPMON INT_AVSPMON ++ ++#define FIQ_ARM_TIMER INT_ARM_TIMER ++#define FIQ_ARM_MAILBOX INT_ARM_MAILBOX ++#define FIQ_ARM_DOORBELL_0 INT_ARM_DOORBELL_0 ++#define FIQ_ARM_DOORBELL_1 INT_ARM_DOORBELL_1 ++#define FIQ_VPU0_HALTED INT_VPU0_HALTED ++#define FIQ_VPU1_HALTED INT_VPU1_HALTED ++#define FIQ_ILLEGAL_TYPE0 INT_ILLEGAL_TYPE0 ++#define FIQ_ILLEGAL_TYPE1 INT_ILLEGAL_TYPE1 ++#define FIQ_PENDING1 INT_PENDING1 ++#define FIQ_PENDING2 INT_PENDING2 ++ ++#define NR_IRQS (64 + 21) ++ ++#endif /* _BCM2708_IRQS_H_ */ +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/irqs.h.orig +@@ -0,0 +1,185 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/irqs.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2003 ARM Limited ++ * Copyright (C) 2000 Deep Blue Solutions Ltd. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <mach/platform.h> ++ ++/* ++ * IRQ interrupts definitions are the same as the INT definitions ++ * held within platform.h ++ */ ++#define IRQ_ARMCTRL_START 0 ++#define IRQ_TIMER0 (IRQ_ARMCTRL_START + INTERRUPT_TIMER0) ++#define IRQ_TIMER1 (IRQ_ARMCTRL_START + INTERRUPT_TIMER1) ++#define IRQ_TIMER2 (IRQ_ARMCTRL_START + INTERRUPT_TIMER2) ++#define IRQ_TIMER3 (IRQ_ARMCTRL_START + INTERRUPT_TIMER3) ++#define IRQ_CODEC0 (IRQ_ARMCTRL_START + INTERRUPT_CODEC0) ++#define IRQ_CODEC1 (IRQ_ARMCTRL_START + INTERRUPT_CODEC1) ++#define IRQ_CODEC2 (IRQ_ARMCTRL_START + INTERRUPT_CODEC2) ++#define IRQ_JPEG (IRQ_ARMCTRL_START + INTERRUPT_JPEG) ++#define IRQ_ISP (IRQ_ARMCTRL_START + INTERRUPT_ISP) ++#define IRQ_USB (IRQ_ARMCTRL_START + INTERRUPT_USB) ++#define IRQ_3D (IRQ_ARMCTRL_START + INTERRUPT_3D) ++#define IRQ_TRANSPOSER (IRQ_ARMCTRL_START + INTERRUPT_TRANSPOSER) ++#define IRQ_MULTICORESYNC0 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC0) ++#define IRQ_MULTICORESYNC1 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC1) ++#define IRQ_MULTICORESYNC2 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC2) ++#define IRQ_MULTICORESYNC3 (IRQ_ARMCTRL_START + INTERRUPT_MULTICORESYNC3) ++#define IRQ_DMA0 (IRQ_ARMCTRL_START + INTERRUPT_DMA0) ++#define IRQ_DMA1 (IRQ_ARMCTRL_START + INTERRUPT_DMA1) ++#define IRQ_DMA2 (IRQ_ARMCTRL_START + INTERRUPT_DMA2) ++#define IRQ_DMA3 (IRQ_ARMCTRL_START + INTERRUPT_DMA3) ++#define IRQ_DMA4 (IRQ_ARMCTRL_START + INTERRUPT_DMA4) ++#define IRQ_DMA5 (IRQ_ARMCTRL_START + INTERRUPT_DMA5) ++#define IRQ_DMA6 (IRQ_ARMCTRL_START + INTERRUPT_DMA6) ++#define IRQ_DMA7 (IRQ_ARMCTRL_START + INTERRUPT_DMA7) ++#define IRQ_DMA8 (IRQ_ARMCTRL_START + INTERRUPT_DMA8) ++#define IRQ_DMA9 (IRQ_ARMCTRL_START + INTERRUPT_DMA9) ++#define IRQ_DMA10 (IRQ_ARMCTRL_START + INTERRUPT_DMA10) ++#define IRQ_DMA11 (IRQ_ARMCTRL_START + INTERRUPT_DMA11) ++#define IRQ_DMA12 (IRQ_ARMCTRL_START + INTERRUPT_DMA12) ++#define IRQ_AUX (IRQ_ARMCTRL_START + INTERRUPT_AUX) ++#define IRQ_ARM (IRQ_ARMCTRL_START + INTERRUPT_ARM) ++#define IRQ_VPUDMA (IRQ_ARMCTRL_START + INTERRUPT_VPUDMA) ++#define IRQ_HOSTPORT (IRQ_ARMCTRL_START + INTERRUPT_HOSTPORT) ++#define IRQ_VIDEOSCALER (IRQ_ARMCTRL_START + INTERRUPT_VIDEOSCALER) ++#define IRQ_CCP2TX (IRQ_ARMCTRL_START + INTERRUPT_CCP2TX) ++#define IRQ_SDC (IRQ_ARMCTRL_START + INTERRUPT_SDC) ++#define IRQ_DSI0 (IRQ_ARMCTRL_START + INTERRUPT_DSI0) ++#define IRQ_AVE (IRQ_ARMCTRL_START + INTERRUPT_AVE) ++#define IRQ_CAM0 (IRQ_ARMCTRL_START + INTERRUPT_CAM0) ++#define IRQ_CAM1 (IRQ_ARMCTRL_START + INTERRUPT_CAM1) ++#define IRQ_HDMI0 (IRQ_ARMCTRL_START + INTERRUPT_HDMI0) ++#define IRQ_HDMI1 (IRQ_ARMCTRL_START + INTERRUPT_HDMI1) ++#define IRQ_PIXELVALVE1 (IRQ_ARMCTRL_START + INTERRUPT_PIXELVALVE1) ++#define IRQ_I2CSPISLV (IRQ_ARMCTRL_START + INTERRUPT_I2CSPISLV) ++#define IRQ_DSI1 (IRQ_ARMCTRL_START + INTERRUPT_DSI1) ++#define IRQ_PWA0 (IRQ_ARMCTRL_START + INTERRUPT_PWA0) ++#define IRQ_PWA1 (IRQ_ARMCTRL_START + INTERRUPT_PWA1) ++#define IRQ_CPR (IRQ_ARMCTRL_START + INTERRUPT_CPR) ++#define IRQ_SMI (IRQ_ARMCTRL_START + INTERRUPT_SMI) ++#define IRQ_GPIO0 (IRQ_ARMCTRL_START + INTERRUPT_GPIO0) ++#define IRQ_GPIO1 (IRQ_ARMCTRL_START + INTERRUPT_GPIO1) ++#define IRQ_GPIO2 (IRQ_ARMCTRL_START + INTERRUPT_GPIO2) ++#define IRQ_GPIO3 (IRQ_ARMCTRL_START + INTERRUPT_GPIO3) ++#define IRQ_I2C (IRQ_ARMCTRL_START + INTERRUPT_I2C) ++#define IRQ_SPI (IRQ_ARMCTRL_START + INTERRUPT_SPI) ++#define IRQ_I2SPCM (IRQ_ARMCTRL_START + INTERRUPT_I2SPCM) ++#define IRQ_SDIO (IRQ_ARMCTRL_START + INTERRUPT_SDIO) ++#define IRQ_UART (IRQ_ARMCTRL_START + INTERRUPT_UART) ++#define IRQ_SLIMBUS (IRQ_ARMCTRL_START + INTERRUPT_SLIMBUS) ++#define IRQ_VEC (IRQ_ARMCTRL_START + INTERRUPT_VEC) ++#define IRQ_CPG (IRQ_ARMCTRL_START + INTERRUPT_CPG) ++#define IRQ_RNG (IRQ_ARMCTRL_START + INTERRUPT_RNG) ++#define IRQ_ARASANSDIO (IRQ_ARMCTRL_START + INTERRUPT_ARASANSDIO) ++#define IRQ_AVSPMON (IRQ_ARMCTRL_START + INTERRUPT_AVSPMON) ++ ++#define IRQ_ARM_TIMER (IRQ_ARMCTRL_START + INTERRUPT_ARM_TIMER) ++#define IRQ_ARM_MAILBOX (IRQ_ARMCTRL_START + INTERRUPT_ARM_MAILBOX) ++#define IRQ_ARM_DOORBELL_0 (IRQ_ARMCTRL_START + INTERRUPT_ARM_DOORBELL_0) ++#define IRQ_ARM_DOORBELL_1 (IRQ_ARMCTRL_START + INTERRUPT_ARM_DOORBELL_1) ++#define IRQ_VPU0_HALTED (IRQ_ARMCTRL_START + INTERRUPT_VPU0_HALTED) ++#define IRQ_VPU1_HALTED (IRQ_ARMCTRL_START + INTERRUPT_VPU1_HALTED) ++#define IRQ_ILLEGAL_TYPE0 (IRQ_ARMCTRL_START + INTERRUPT_ILLEGAL_TYPE0) ++#define IRQ_ILLEGAL_TYPE1 (IRQ_ARMCTRL_START + INTERRUPT_ILLEGAL_TYPE1) ++#define IRQ_PENDING1 (IRQ_ARMCTRL_START + INTERRUPT_PENDING1) ++#define IRQ_PENDING2 (IRQ_ARMCTRL_START + INTERRUPT_PENDING2) ++ ++/* ++ * FIQ interrupts definitions are the same as the INT definitions. ++ */ ++#define FIQ_TIMER0 INT_TIMER0 ++#define FIQ_TIMER1 INT_TIMER1 ++#define FIQ_TIMER2 INT_TIMER2 ++#define FIQ_TIMER3 INT_TIMER3 ++#define FIQ_CODEC0 INT_CODEC0 ++#define FIQ_CODEC1 INT_CODEC1 ++#define FIQ_CODEC2 INT_CODEC2 ++#define FIQ_JPEG INT_JPEG ++#define FIQ_ISP INT_ISP ++#define FIQ_USB INT_USB ++#define FIQ_3D INT_3D ++#define FIQ_TRANSPOSER INT_TRANSPOSER ++#define FIQ_MULTICORESYNC0 INT_MULTICORESYNC0 ++#define FIQ_MULTICORESYNC1 INT_MULTICORESYNC1 ++#define FIQ_MULTICORESYNC2 INT_MULTICORESYNC2 ++#define FIQ_MULTICORESYNC3 INT_MULTICORESYNC3 ++#define FIQ_DMA0 INT_DMA0 ++#define FIQ_DMA1 INT_DMA1 ++#define FIQ_DMA2 INT_DMA2 ++#define FIQ_DMA3 INT_DMA3 ++#define FIQ_DMA4 INT_DMA4 ++#define FIQ_DMA5 INT_DMA5 ++#define FIQ_DMA6 INT_DMA6 ++#define FIQ_DMA7 INT_DMA7 ++#define FIQ_DMA8 INT_DMA8 ++#define FIQ_DMA9 INT_DMA9 ++#define FIQ_DMA10 INT_DMA10 ++#define FIQ_DMA11 INT_DMA11 ++#define FIQ_DMA12 INT_DMA12 ++#define FIQ_AUX INT_AUX ++#define FIQ_ARM INT_ARM ++#define FIQ_VPUDMA INT_VPUDMA ++#define FIQ_HOSTPORT INT_HOSTPORT ++#define FIQ_VIDEOSCALER INT_VIDEOSCALER ++#define FIQ_CCP2TX INT_CCP2TX ++#define FIQ_SDC INT_SDC ++#define FIQ_DSI0 INT_DSI0 ++#define FIQ_AVE INT_AVE ++#define FIQ_CAM0 INT_CAM0 ++#define FIQ_CAM1 INT_CAM1 ++#define FIQ_HDMI0 INT_HDMI0 ++#define FIQ_HDMI1 INT_HDMI1 ++#define FIQ_PIXELVALVE1 INT_PIXELVALVE1 ++#define FIQ_I2CSPISLV INT_I2CSPISLV ++#define FIQ_DSI1 INT_DSI1 ++#define FIQ_PWA0 INT_PWA0 ++#define FIQ_PWA1 INT_PWA1 ++#define FIQ_CPR INT_CPR ++#define FIQ_SMI INT_SMI ++#define FIQ_GPIO0 INT_GPIO0 ++#define FIQ_GPIO1 INT_GPIO1 ++#define FIQ_GPIO2 INT_GPIO2 ++#define FIQ_GPIO3 INT_GPIO3 ++#define FIQ_I2C INT_I2C ++#define FIQ_SPI INT_SPI ++#define FIQ_I2SPCM INT_I2SPCM ++#define FIQ_SDIO INT_SDIO ++#define FIQ_UART INT_UART ++#define FIQ_SLIMBUS INT_SLIMBUS ++#define FIQ_VEC INT_VEC ++#define FIQ_CPG INT_CPG ++#define FIQ_RNG INT_RNG ++#define FIQ_ARASANSDIO INT_ARASANSDIO ++#define FIQ_AVSPMON INT_AVSPMON ++ ++#define FIQ_ARM_TIMER INT_ARM_TIMER ++#define FIQ_ARM_MAILBOX INT_ARM_MAILBOX ++#define FIQ_ARM_DOORBELL_0 INT_ARM_DOORBELL_0 ++#define FIQ_ARM_DOORBELL_1 INT_ARM_DOORBELL_1 ++#define FIQ_VPU0_HALTED INT_VPU0_HALTED ++#define FIQ_VPU1_HALTED INT_VPU1_HALTED ++#define FIQ_ILLEGAL_TYPE0 INT_ILLEGAL_TYPE0 ++#define FIQ_ILLEGAL_TYPE1 INT_ILLEGAL_TYPE1 ++#define FIQ_PENDING1 INT_PENDING1 ++#define FIQ_PENDING2 INT_PENDING2 ++ ++#define NR_IRQS (64 + 21) +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/memory.h +@@ -0,0 +1,59 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/memory.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __ASM_ARCH_MEMORY_H ++#define __ASM_ARCH_MEMORY_H ++ ++/* Memory overview: ++ ++ [ARMcore] <--virtual addr--> ++ [ARMmmu] <--physical addr--> ++ [GERTmap] <--bus add--> ++ [VCperiph] ++ ++*/ ++ ++/* ++ * Physical DRAM offset. ++ */ ++#define PHYS_OFFSET UL(0x00000000) ++#define ARMMEM_OFFSET UL(0x00000000) /* offset in VC of ARM memory */ ++#define _REAL_BUS_OFFSET UL(0xC0000000) /* don't use L1 or L2 caches */ ++ ++/* We're using the memory at 64M in the VideoCore for Linux - this adjustment ++ * will provide the offset into this area as well as setting the bits that ++ * stop the L1 and L2 cache from being used ++ * ++ * WARNING: this only works because the ARM is given memory at a fixed location ++ * (ARMMEM_OFFSET) ++ */ ++#define BUS_OFFSET (ARMMEM_OFFSET + _REAL_BUS_OFFSET) ++#define __virt_to_bus(x) ((x) + (BUS_OFFSET - PAGE_OFFSET)) ++#define __bus_to_virt(x) ((x) - (BUS_OFFSET - PAGE_OFFSET)) ++#define __pfn_to_bus(x) (__pfn_to_phys(x) + (BUS_OFFSET - PHYS_OFFSET)) ++#define __bus_to_pfn(x) __phys_to_pfn((x) - (BUS_OFFSET - PHYS_OFFSET)) ++ ++/* ++ * Consistent DMA area set to 2M. Framebuffer now allocated on host ++ */ ++ ++ ++#define CONSISTENT_DMA_SIZE 0x00200000 ++ ++#endif +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/platform.h +@@ -0,0 +1,210 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/platform.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _BCM2708_PLATFORM_H ++#define _BCM2708_PLATFORM_H ++ ++ ++/* macros to get at IO space when running virtually */ ++#define IO_ADDRESS(x) (((x) & 0x0fffffff) + (((x) >> 4) & 0x0f000000) + 0xf0000000) ++ ++#define __io_address(a) __io(IO_ADDRESS(a)) ++ ++ ++/* ++ * SDRAM ++ */ ++#define BCM2708_SDRAM_BASE 0x00000000 ++ ++/* ++ * Logic expansion modules ++ * ++ */ ++ ++ ++/* ------------------------------------------------------------------------ ++ * BCM2708 ARMCTRL Registers ++ * ------------------------------------------------------------------------ ++ */ ++ ++#define HW_REGISTER_RW(addr) (addr) ++#define HW_REGISTER_RO(addr) (addr) ++ ++#include "arm_control.h" ++#undef ARM_BASE ++ ++/* ++ * Definitions and addresses for the ARM CONTROL logic ++ * This file is manually generated. ++ */ ++ ++#define BCM2708_PERI_BASE 0x20000000 ++#define ST_BASE (BCM2708_PERI_BASE + 0x3000) /* System Timer */ ++#define DMA_BASE (BCM2708_PERI_BASE + 0x7000) /* DMA controller */ ++#define ARM_BASE (BCM2708_PERI_BASE + 0xB000) /* BCM2708 ARM control block */ ++#define PM_BASE (BCM2708_PERI_BASE + 0x100000) /* Power Management, Reset controller and Watchdog registers */ ++#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO */ ++#define UART0_BASE (BCM2708_PERI_BASE + 0x201000) /* Uart 0 */ ++#define MMCI0_BASE (BCM2708_PERI_BASE + 0x202000) /* MMC interface */ ++#define UART1_BASE (BCM2708_PERI_BASE + 0x215000) /* Uart 1 */ ++#define EMMC_BASE (BCM2708_PERI_BASE + 0x300000) /* eMMC interface */ ++#define SMI_BASE (BCM2708_PERI_BASE + 0x600000) /* SMI */ ++#define USB_BASE (BCM2708_PERI_BASE + 0x980000) /* DTC_OTG USB controller */ ++#define MCORE_BASE (BCM2708_PERI_BASE + 0x0000) /* Fake frame buffer device (actually the multicore sync block*/ ++ ++#define ARMCTRL_BASE (ARM_BASE + 0x000) ++#define ARMCTRL_IC_BASE (ARM_BASE + 0x200) /* ARM interrupt controller */ ++#define ARMCTRL_TIMER0_1_BASE (ARM_BASE + 0x400) /* Timer 0 and 1 */ ++#define ARMCTRL_0_SBM_BASE (ARM_BASE + 0x800) /* User 0 (ARM)'s Semaphores Doorbells and Mailboxes */ ++ ++ ++/* ++ * Interrupt assignments ++ */ ++ ++#define ARM_IRQ1_BASE 0 ++#define INTERRUPT_TIMER0 (ARM_IRQ1_BASE + 0) ++#define INTERRUPT_TIMER1 (ARM_IRQ1_BASE + 1) ++#define INTERRUPT_TIMER2 (ARM_IRQ1_BASE + 2) ++#define INTERRUPT_TIMER3 (ARM_IRQ1_BASE + 3) ++#define INTERRUPT_CODEC0 (ARM_IRQ1_BASE + 4) ++#define INTERRUPT_CODEC1 (ARM_IRQ1_BASE + 5) ++#define INTERRUPT_CODEC2 (ARM_IRQ1_BASE + 6) ++#define INTERRUPT_VC_JPEG (ARM_IRQ1_BASE + 7) ++#define INTERRUPT_ISP (ARM_IRQ1_BASE + 8) ++#define INTERRUPT_VC_USB (ARM_IRQ1_BASE + 9) ++#define INTERRUPT_VC_3D (ARM_IRQ1_BASE + 10) ++#define INTERRUPT_TRANSPOSER (ARM_IRQ1_BASE + 11) ++#define INTERRUPT_MULTICORESYNC0 (ARM_IRQ1_BASE + 12) ++#define INTERRUPT_MULTICORESYNC1 (ARM_IRQ1_BASE + 13) ++#define INTERRUPT_MULTICORESYNC2 (ARM_IRQ1_BASE + 14) ++#define INTERRUPT_MULTICORESYNC3 (ARM_IRQ1_BASE + 15) ++#define INTERRUPT_DMA0 (ARM_IRQ1_BASE + 16) ++#define INTERRUPT_DMA1 (ARM_IRQ1_BASE + 17) ++#define INTERRUPT_VC_DMA2 (ARM_IRQ1_BASE + 18) ++#define INTERRUPT_VC_DMA3 (ARM_IRQ1_BASE + 19) ++#define INTERRUPT_DMA4 (ARM_IRQ1_BASE + 20) ++#define INTERRUPT_DMA5 (ARM_IRQ1_BASE + 21) ++#define INTERRUPT_DMA6 (ARM_IRQ1_BASE + 22) ++#define INTERRUPT_DMA7 (ARM_IRQ1_BASE + 23) ++#define INTERRUPT_DMA8 (ARM_IRQ1_BASE + 24) ++#define INTERRUPT_DMA9 (ARM_IRQ1_BASE + 25) ++#define INTERRUPT_DMA10 (ARM_IRQ1_BASE + 26) ++#define INTERRUPT_DMA11 (ARM_IRQ1_BASE + 27) ++#define INTERRUPT_DMA12 (ARM_IRQ1_BASE + 28) ++#define INTERRUPT_AUX (ARM_IRQ1_BASE + 29) ++#define INTERRUPT_ARM (ARM_IRQ1_BASE + 30) ++#define INTERRUPT_VPUDMA (ARM_IRQ1_BASE + 31) ++ ++#define ARM_IRQ2_BASE 32 ++#define INTERRUPT_HOSTPORT (ARM_IRQ2_BASE + 0) ++#define INTERRUPT_VIDEOSCALER (ARM_IRQ2_BASE + 1) ++#define INTERRUPT_CCP2TX (ARM_IRQ2_BASE + 2) ++#define INTERRUPT_SDC (ARM_IRQ2_BASE + 3) ++#define INTERRUPT_DSI0 (ARM_IRQ2_BASE + 4) ++#define INTERRUPT_AVE (ARM_IRQ2_BASE + 5) ++#define INTERRUPT_CAM0 (ARM_IRQ2_BASE + 6) ++#define INTERRUPT_CAM1 (ARM_IRQ2_BASE + 7) ++#define INTERRUPT_HDMI0 (ARM_IRQ2_BASE + 8) ++#define INTERRUPT_HDMI1 (ARM_IRQ2_BASE + 9) ++#define INTERRUPT_PIXELVALVE1 (ARM_IRQ2_BASE + 10) ++#define INTERRUPT_I2CSPISLV (ARM_IRQ2_BASE + 11) ++#define INTERRUPT_DSI1 (ARM_IRQ2_BASE + 12) ++#define INTERRUPT_PWA0 (ARM_IRQ2_BASE + 13) ++#define INTERRUPT_PWA1 (ARM_IRQ2_BASE + 14) ++#define INTERRUPT_CPR (ARM_IRQ2_BASE + 15) ++#define INTERRUPT_SMI (ARM_IRQ2_BASE + 16) ++#define INTERRUPT_GPIO0 (ARM_IRQ2_BASE + 17) ++#define INTERRUPT_GPIO1 (ARM_IRQ2_BASE + 18) ++#define INTERRUPT_GPIO2 (ARM_IRQ2_BASE + 19) ++#define INTERRUPT_GPIO3 (ARM_IRQ2_BASE + 20) ++#define INTERRUPT_VC_I2C (ARM_IRQ2_BASE + 21) ++#define INTERRUPT_VC_SPI (ARM_IRQ2_BASE + 22) ++#define INTERRUPT_VC_I2SPCM (ARM_IRQ2_BASE + 23) ++#define INTERRUPT_VC_SDIO (ARM_IRQ2_BASE + 24) ++#define INTERRUPT_VC_UART (ARM_IRQ2_BASE + 25) ++#define INTERRUPT_SLIMBUS (ARM_IRQ2_BASE + 26) ++#define INTERRUPT_VEC (ARM_IRQ2_BASE + 27) ++#define INTERRUPT_CPG (ARM_IRQ2_BASE + 28) ++#define INTERRUPT_RNG (ARM_IRQ2_BASE + 29) ++#define INTERRUPT_VC_ARASANSDIO (ARM_IRQ2_BASE + 30) ++#define INTERRUPT_AVSPMON (ARM_IRQ2_BASE + 31) ++ ++#define ARM_IRQ0_BASE 64 ++#define INTERRUPT_ARM_TIMER (ARM_IRQ0_BASE + 0) ++#define INTERRUPT_ARM_MAILBOX (ARM_IRQ0_BASE + 1) ++#define INTERRUPT_ARM_DOORBELL_0 (ARM_IRQ0_BASE + 2) ++#define INTERRUPT_ARM_DOORBELL_1 (ARM_IRQ0_BASE + 3) ++#define INTERRUPT_VPU0_HALTED (ARM_IRQ0_BASE + 4) ++#define INTERRUPT_VPU1_HALTED (ARM_IRQ0_BASE + 5) ++#define INTERRUPT_ILLEGAL_TYPE0 (ARM_IRQ0_BASE + 6) ++#define INTERRUPT_ILLEGAL_TYPE1 (ARM_IRQ0_BASE + 7) ++#define INTERRUPT_PENDING1 (ARM_IRQ0_BASE + 8) ++#define INTERRUPT_PENDING2 (ARM_IRQ0_BASE + 9) ++#define INTERRUPT_JPEG (ARM_IRQ0_BASE + 10) ++#define INTERRUPT_USB (ARM_IRQ0_BASE + 11) ++#define INTERRUPT_3D (ARM_IRQ0_BASE + 12) ++#define INTERRUPT_DMA2 (ARM_IRQ0_BASE + 13) ++#define INTERRUPT_DMA3 (ARM_IRQ0_BASE + 14) ++#define INTERRUPT_I2C (ARM_IRQ0_BASE + 15) ++#define INTERRUPT_SPI (ARM_IRQ0_BASE + 16) ++#define INTERRUPT_I2SPCM (ARM_IRQ0_BASE + 17) ++#define INTERRUPT_SDIO (ARM_IRQ0_BASE + 18) ++#define INTERRUPT_UART (ARM_IRQ0_BASE + 19) ++#define INTERRUPT_ARASANSDIO (ARM_IRQ0_BASE + 20) ++ ++#define MAXIRQNUM (32 + 32 + 20) ++#define MAXFIQNUM (32 + 32 + 20) ++ ++#define MAX_TIMER 2 ++#define MAX_PERIOD 699050 ++#define TICKS_PER_uSEC 1 ++ ++/* ++ * These are useconds NOT ticks. ++ * ++ */ ++#define mSEC_1 1000 ++#define mSEC_5 (mSEC_1 * 5) ++#define mSEC_10 (mSEC_1 * 10) ++#define mSEC_25 (mSEC_1 * 25) ++#define SEC_1 (mSEC_1 * 1000) ++ ++/* ++ * Watchdog ++ */ ++#define PM_RSTC (PM_BASE+0x1c) ++#define PM_WDOG (PM_BASE+0x24) ++ ++#define PM_WDOG_RESET 0000000000 ++#define PM_PASSWORD 0x5a000000 ++#define PM_WDOG_TIME_SET 0x000fffff ++#define PM_RSTC_WRCFG_CLR 0xffffffcf ++#define PM_RSTC_WRCFG_SET 0x00000030 ++#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 ++#define PM_RSTC_RESET 0x00000102 ++ ++ ++ ++ ++ ++#endif ++ ++/* END */ +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/platform.h.orig +@@ -0,0 +1,210 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/platform.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _BCM2708_PLATFORM_H ++#define _BCM2708_PLATFORM_H ++ ++ ++/* macros to get at IO space when running virtually */ ++#define IO_ADDRESS(x) (((x) & 0x0fffffff) + (((x) >> 4) & 0x0f000000) + 0xf0000000) ++ ++#define __io_address(a) __io(IO_ADDRESS(a)) ++ ++ ++/* ++ * SDRAM ++ */ ++#define BCM2708_SDRAM_BASE 0x00000000 ++ ++/* ++ * Logic expansion modules ++ * ++ */ ++ ++ ++/* ------------------------------------------------------------------------ ++ * BCM2708 ARMCTRL Registers ++ * ------------------------------------------------------------------------ ++ */ ++ ++#define HW_REGISTER_RW(addr) (addr) ++#define HW_REGISTER_RO(addr) (addr) ++ ++#include "arm_control.h" ++#undef ARM_BASE ++ ++/* ++ * Definitions and addresses for the ARM CONTROL logic ++ * This file is manually generated. ++ */ ++ ++#define BCM2708_PERI_BASE 0x20000000 ++#define ST_BASE (BCM2708_PERI_BASE + 0x3000) /* System Timer */ ++#define DMA_BASE (BCM2708_PERI_BASE + 0x7000) /* DMA controller */ ++#define ARM_BASE (BCM2708_PERI_BASE + 0xB000) /* BCM2708 ARM control block */ ++#define PM_BASE (BCM2708_PERI_BASE + 0x100000) /* Power Management, Reset controller and Watchdog registers */ ++#define GPIO_BASE (BCM2708_PERI_BASE + 0x200000) /* GPIO */ ++#define UART0_BASE (BCM2708_PERI_BASE + 0x201000) /* Uart 0 */ ++#define MMCI0_BASE (BCM2708_PERI_BASE + 0x202000) /* MMC interface */ ++#define UART1_BASE (BCM2708_PERI_BASE + 0x215000) /* Uart 1 */ ++#define EMMC_BASE (BCM2708_PERI_BASE + 0x300000) /* eMMC interface */ ++#define SMI_BASE (BCM2708_PERI_BASE + 0x600000) /* SMI */ ++#define USB_BASE (BCM2708_PERI_BASE + 0x980000) /* DTC_OTG USB controller */ ++ ++ ++#define ARMCTRL_BASE (ARM_BASE + 0x000) ++#define ARMCTRL_IC_BASE (ARM_BASE + 0x200) /* ARM interrupt controller */ ++#define ARMCTRL_TIMER0_1_BASE (ARM_BASE + 0x400) /* Timer 0 and 1 */ ++#define ARMCTRL_0_SBM_BASE (ARM_BASE + 0x800) /* User 0 (ARM)'s Semaphores Doorbells and Mailboxes */ ++ ++ ++/* ++ * Interrupt assignments ++ */ ++ ++#define ARM_IRQ1_BASE 0 ++#define INTERRUPT_TIMER0 (ARM_IRQ1_BASE + 0) ++#define INTERRUPT_TIMER1 (ARM_IRQ1_BASE + 1) ++#define INTERRUPT_TIMER2 (ARM_IRQ1_BASE + 2) ++#define INTERRUPT_TIMER3 (ARM_IRQ1_BASE + 3) ++#define INTERRUPT_CODEC0 (ARM_IRQ1_BASE + 4) ++#define INTERRUPT_CODEC1 (ARM_IRQ1_BASE + 5) ++#define INTERRUPT_CODEC2 (ARM_IRQ1_BASE + 6) ++#define INTERRUPT_VC_JPEG (ARM_IRQ1_BASE + 7) ++#define INTERRUPT_ISP (ARM_IRQ1_BASE + 8) ++#define INTERRUPT_VC_USB (ARM_IRQ1_BASE + 9) ++#define INTERRUPT_VC_3D (ARM_IRQ1_BASE + 10) ++#define INTERRUPT_TRANSPOSER (ARM_IRQ1_BASE + 11) ++#define INTERRUPT_MULTICORESYNC0 (ARM_IRQ1_BASE + 12) ++#define INTERRUPT_MULTICORESYNC1 (ARM_IRQ1_BASE + 13) ++#define INTERRUPT_MULTICORESYNC2 (ARM_IRQ1_BASE + 14) ++#define INTERRUPT_MULTICORESYNC3 (ARM_IRQ1_BASE + 15) ++#define INTERRUPT_DMA0 (ARM_IRQ1_BASE + 16) ++#define INTERRUPT_DMA1 (ARM_IRQ1_BASE + 17) ++#define INTERRUPT_VC_DMA2 (ARM_IRQ1_BASE + 18) ++#define INTERRUPT_VC_DMA3 (ARM_IRQ1_BASE + 19) ++#define INTERRUPT_DMA4 (ARM_IRQ1_BASE + 20) ++#define INTERRUPT_DMA5 (ARM_IRQ1_BASE + 21) ++#define INTERRUPT_DMA6 (ARM_IRQ1_BASE + 22) ++#define INTERRUPT_DMA7 (ARM_IRQ1_BASE + 23) ++#define INTERRUPT_DMA8 (ARM_IRQ1_BASE + 24) ++#define INTERRUPT_DMA9 (ARM_IRQ1_BASE + 25) ++#define INTERRUPT_DMA10 (ARM_IRQ1_BASE + 26) ++#define INTERRUPT_DMA11 (ARM_IRQ1_BASE + 27) ++#define INTERRUPT_DMA12 (ARM_IRQ1_BASE + 28) ++#define INTERRUPT_AUX (ARM_IRQ1_BASE + 29) ++#define INTERRUPT_ARM (ARM_IRQ1_BASE + 30) ++#define INTERRUPT_VPUDMA (ARM_IRQ1_BASE + 31) ++ ++#define ARM_IRQ2_BASE 32 ++#define INTERRUPT_HOSTPORT (ARM_IRQ2_BASE + 0) ++#define INTERRUPT_VIDEOSCALER (ARM_IRQ2_BASE + 1) ++#define INTERRUPT_CCP2TX (ARM_IRQ2_BASE + 2) ++#define INTERRUPT_SDC (ARM_IRQ2_BASE + 3) ++#define INTERRUPT_DSI0 (ARM_IRQ2_BASE + 4) ++#define INTERRUPT_AVE (ARM_IRQ2_BASE + 5) ++#define INTERRUPT_CAM0 (ARM_IRQ2_BASE + 6) ++#define INTERRUPT_CAM1 (ARM_IRQ2_BASE + 7) ++#define INTERRUPT_HDMI0 (ARM_IRQ2_BASE + 8) ++#define INTERRUPT_HDMI1 (ARM_IRQ2_BASE + 9) ++#define INTERRUPT_PIXELVALVE1 (ARM_IRQ2_BASE + 10) ++#define INTERRUPT_I2CSPISLV (ARM_IRQ2_BASE + 11) ++#define INTERRUPT_DSI1 (ARM_IRQ2_BASE + 12) ++#define INTERRUPT_PWA0 (ARM_IRQ2_BASE + 13) ++#define INTERRUPT_PWA1 (ARM_IRQ2_BASE + 14) ++#define INTERRUPT_CPR (ARM_IRQ2_BASE + 15) ++#define INTERRUPT_SMI (ARM_IRQ2_BASE + 16) ++#define INTERRUPT_GPIO0 (ARM_IRQ2_BASE + 17) ++#define INTERRUPT_GPIO1 (ARM_IRQ2_BASE + 18) ++#define INTERRUPT_GPIO2 (ARM_IRQ2_BASE + 19) ++#define INTERRUPT_GPIO3 (ARM_IRQ2_BASE + 20) ++#define INTERRUPT_VC_I2C (ARM_IRQ2_BASE + 21) ++#define INTERRUPT_VC_SPI (ARM_IRQ2_BASE + 22) ++#define INTERRUPT_VC_I2SPCM (ARM_IRQ2_BASE + 23) ++#define INTERRUPT_VC_SDIO (ARM_IRQ2_BASE + 24) ++#define INTERRUPT_VC_UART (ARM_IRQ2_BASE + 25) ++#define INTERRUPT_SLIMBUS (ARM_IRQ2_BASE + 26) ++#define INTERRUPT_VEC (ARM_IRQ2_BASE + 27) ++#define INTERRUPT_CPG (ARM_IRQ2_BASE + 28) ++#define INTERRUPT_RNG (ARM_IRQ2_BASE + 29) ++#define INTERRUPT_VC_ARASANSDIO (ARM_IRQ2_BASE + 30) ++#define INTERRUPT_AVSPMON (ARM_IRQ2_BASE + 31) ++ ++#define ARM_IRQ0_BASE 64 ++#define INTERRUPT_ARM_TIMER (ARM_IRQ0_BASE + 0) ++#define INTERRUPT_ARM_MAILBOX (ARM_IRQ0_BASE + 1) ++#define INTERRUPT_ARM_DOORBELL_0 (ARM_IRQ0_BASE + 2) ++#define INTERRUPT_ARM_DOORBELL_1 (ARM_IRQ0_BASE + 3) ++#define INTERRUPT_VPU0_HALTED (ARM_IRQ0_BASE + 4) ++#define INTERRUPT_VPU1_HALTED (ARM_IRQ0_BASE + 5) ++#define INTERRUPT_ILLEGAL_TYPE0 (ARM_IRQ0_BASE + 6) ++#define INTERRUPT_ILLEGAL_TYPE1 (ARM_IRQ0_BASE + 7) ++#define INTERRUPT_PENDING1 (ARM_IRQ0_BASE + 8) ++#define INTERRUPT_PENDING2 (ARM_IRQ0_BASE + 9) ++#define INTERRUPT_JPEG (ARM_IRQ0_BASE + 10) ++#define INTERRUPT_USB (ARM_IRQ0_BASE + 11) ++#define INTERRUPT_3D (ARM_IRQ0_BASE + 12) ++#define INTERRUPT_DMA2 (ARM_IRQ0_BASE + 13) ++#define INTERRUPT_DMA3 (ARM_IRQ0_BASE + 14) ++#define INTERRUPT_I2C (ARM_IRQ0_BASE + 15) ++#define INTERRUPT_SPI (ARM_IRQ0_BASE + 16) ++#define INTERRUPT_I2SPCM (ARM_IRQ0_BASE + 17) ++#define INTERRUPT_SDIO (ARM_IRQ0_BASE + 18) ++#define INTERRUPT_UART (ARM_IRQ0_BASE + 19) ++#define INTERRUPT_ARASANSDIO (ARM_IRQ0_BASE + 20) ++ ++#define MAXIRQNUM (32 + 32 + 20) ++#define MAXFIQNUM (32 + 32 + 20) ++ ++#define MAX_TIMER 2 ++#define MAX_PERIOD 699050 ++#define TICKS_PER_uSEC 1 ++ ++/* ++ * These are useconds NOT ticks. ++ * ++ */ ++#define mSEC_1 1000 ++#define mSEC_5 (mSEC_1 * 5) ++#define mSEC_10 (mSEC_1 * 10) ++#define mSEC_25 (mSEC_1 * 25) ++#define SEC_1 (mSEC_1 * 1000) ++ ++/* ++ * Watchdog ++ */ ++#define PM_RSTC (PM_BASE+0x1c) ++#define PM_WDOG (PM_BASE+0x24) ++ ++#define PM_WDOG_RESET 0000000000 ++#define PM_PASSWORD 0x5a000000 ++#define PM_WDOG_TIME_SET 0x000fffff ++#define PM_RSTC_WRCFG_CLR 0xffffffcf ++#define PM_RSTC_WRCFG_SET 0x00000030 ++#define PM_RSTC_WRCFG_FULL_RESET 0x00000020 ++#define PM_RSTC_RESET 0x00000102 ++ ++ ++ ++ ++ ++#endif ++ ++/* END */ +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/power.h +@@ -0,0 +1,26 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/power.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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. ++ * ++ * This device provides a shared mechanism for controlling the power to ++ * VideoCore subsystems. ++ */ ++ ++#ifndef _MACH_BCM2708_POWER_H ++#define _MACH_BCM2708_POWER_H ++ ++#include <linux/types.h> ++#include <mach/arm_power.h> ++ ++typedef unsigned int BCM_POWER_HANDLE_T; ++ ++extern int bcm_power_open(BCM_POWER_HANDLE_T *handle); ++extern int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request); ++extern int bcm_power_close(BCM_POWER_HANDLE_T handle); ++ ++#endif +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/system.h +@@ -0,0 +1,53 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/system.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2003 ARM Limited ++ * Copyright (C) 2000 Deep Blue Solutions Ltd ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef __ASM_ARCH_SYSTEM_H ++#define __ASM_ARCH_SYSTEM_H ++ ++#include <linux/io.h> ++#include <mach/hardware.h> ++#include <mach/platform.h> ++ ++static inline void arch_idle(void) ++{ ++ /* ++ * This should do all the clock switching ++ * and wait for interrupt tricks ++ */ ++ cpu_do_idle(); ++} ++ ++static inline void arch_reset(char mode, const char *cmd) ++{ ++ uint32_t pm_rstc, pm_wdog; ++ uint32_t timeout = 10; ++ ++ /* Setup watchdog for reset */ ++ pm_rstc = readl(IO_ADDRESS(PM_RSTC)); ++ ++ pm_wdog = PM_PASSWORD | (timeout & PM_WDOG_TIME_SET); // watchdog timer = timer clock / 16; need password (31:16) + value (11:0) ++ pm_rstc = PM_PASSWORD | (pm_rstc & PM_RSTC_WRCFG_CLR) | PM_RSTC_WRCFG_FULL_RESET; ++ ++ writel(pm_wdog, IO_ADDRESS(PM_WDOG)); ++ writel(pm_rstc, IO_ADDRESS(PM_RSTC)); ++} ++ ++#endif +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/timex.h +@@ -0,0 +1,23 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/timex.h ++ * ++ * BCM2708 sysem clock frequency ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#define CLOCK_TICK_RATE (1000000) +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/uncompress.h +@@ -0,0 +1,49 @@ ++/* ++ * arch/arm/mach-bcn2708/include/mach/uncompress.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * Copyright (C) 2003 ARM Limited ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <linux/io.h> ++#include <mach/hardware.h> ++ ++#define BCM2708_UART_DR __io_address(UART0_BASE + 0x00) ++#define BCM2708_UART_FR __io_address(UART0_BASE + 0x18) ++ ++/* ++ * This does not append a newline ++ */ ++static inline void putc(int c) ++{ ++ while (readl(BCM2708_UART_FR) & (1 << 5)) ++ barrier(); ++ ++ writel(c, BCM2708_UART_DR); ++} ++ ++static inline void flush(void) ++{ ++ while (readl(BCM2708_UART_FR) & (1 << 3)) ++ barrier(); ++} ++ ++/* ++ * nothing to do ++ */ ++#define arch_decomp_setup() ++#define arch_decomp_wdog() +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/vc_mem.h +@@ -0,0 +1,34 @@ ++/***************************************************************************** ++* Copyright 2010 - 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#if !defined( VC_MEM_H ) ++#define VC_MEM_H ++ ++#include <linux/ioctl.h> ++ ++#define VC_MEM_IOC_MAGIC 'v' ++ ++#define VC_MEM_IOC_MEM_PHYS_ADDR _IOR( VC_MEM_IOC_MAGIC, 0, unsigned long ) ++#define VC_MEM_IOC_MEM_SIZE _IOR( VC_MEM_IOC_MAGIC, 1, unsigned int ) ++ ++#if defined( __KERNEL__ ) ++#define VC_MEM_TO_ARM_ADDR_MASK 0x3FFFFFFF ++ ++extern unsigned long mm_vc_mem_phys_addr; ++extern unsigned int mm_vc_mem_size; ++extern int vc_mem_get_current_size( void ); ++#endif ++ ++#endif /* VC_MEM_H */ ++ +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/vcio.h +@@ -0,0 +1,42 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/vcio.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#ifndef _MACH_BCM2708_VCIO_H ++#define _MACH_BCM2708_VCIO_H ++ ++/* Routines to handle I/O via the VideoCore "ARM control" registers ++ * (semaphores, doorbells, mailboxes) ++ */ ++ ++#define BCM_VCIO_DRIVER_NAME "bcm2708_vcio" ++ ++/* Constants shared with the ARM identifying separate mailbox channels */ ++#define MBOX_CHAN_POWER 0 /* for use by the power management interface */ ++#define MBOX_CHAN_FB 1 /* for use by the frame buffer */ ++#define MBOX_CHAN_VUART 2 /* for use by the virtual UART */ ++#define MBOX_CHAN_VCHIQ 3 /* for use by the VCHIQ interface */ ++#define MBOX_CHAN_LEDS 4 /* for use by the leds interface */ ++#define MBOX_CHAN_BUTTONS 5 /* for use by the buttons interface */ ++#define MBOX_CHAN_TOUCH 6 /* for use by the touchscreen interface */ ++#define MBOX_CHAN_COUNT 7 ++ ++extern int /*rc*/ bcm_mailbox_read(unsigned chan, uint32_t *data28); ++extern int /*rc*/ bcm_mailbox_write(unsigned chan, uint32_t data28); ++ ++#endif +--- /dev/null ++++ b/arch/arm/mach-bcm2708/include/mach/vmalloc.h +@@ -0,0 +1,20 @@ ++/* ++ * arch/arm/mach-bcm2708/include/mach/vmalloc.h ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++#define VMALLOC_END (0xd8000000) +--- /dev/null ++++ b/arch/arm/mach-bcm2708/power.c +@@ -0,0 +1,193 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/power.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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. ++ * ++ * This device provides a shared mechanism for controlling the power to ++ * VideoCore subsystems. ++ */ ++ ++#include <linux/module.h> ++#include <linux/semaphore.h> ++#include <linux/bug.h> ++#include <mach/power.h> ++#include <mach/vcio.h> ++#include <mach/arm_power.h> ++ ++#define DRIVER_NAME "bcm2708_power" ++ ++#define BCM_POWER_MAXCLIENTS 4 ++#define BCM_POWER_NOCLIENT (1<<31) ++ ++/* Some drivers expect there devices to be permanently powered */ ++#define BCM_POWER_ALWAYS_ON (BCM_POWER_USB) ++ ++#if 1 ++#define DPRINTK printk ++#else ++#define DPRINTK if (0) printk ++#endif ++ ++struct state_struct { ++ uint32_t global_request; ++ uint32_t client_request[BCM_POWER_MAXCLIENTS]; ++ struct semaphore client_mutex; ++ struct semaphore mutex; ++} g_state; ++ ++int bcm_power_open(BCM_POWER_HANDLE_T *handle) ++{ ++ BCM_POWER_HANDLE_T i; ++ int ret = -EBUSY; ++ ++ down(&g_state.client_mutex); ++ ++ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) { ++ if (g_state.client_request[i] == BCM_POWER_NOCLIENT) { ++ g_state.client_request[i] = BCM_POWER_NONE; ++ *handle = i; ++ ret = 0; ++ break; ++ } ++ } ++ ++ up(&g_state.client_mutex); ++ ++ DPRINTK("bcm_power_open() -> %d\n", *handle); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(bcm_power_open); ++ ++int bcm_power_request(BCM_POWER_HANDLE_T handle, uint32_t request) ++{ ++ int rc = 0; ++ ++ DPRINTK("bcm_power_request(%d, %x)\n", handle, request); ++ ++ if ((handle < BCM_POWER_MAXCLIENTS) && ++ (g_state.client_request[handle] != BCM_POWER_NOCLIENT)) { ++ if (down_interruptible(&g_state.mutex) != 0) { ++ DPRINTK("bcm_power_request -> interrupted\n"); ++ return -EINTR; ++ } ++ ++ if (request != g_state.client_request[handle]) { ++ uint32_t others_request = 0; ++ uint32_t global_request; ++ BCM_POWER_HANDLE_T i; ++ ++ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) { ++ if (i != handle) ++ others_request |= ++ g_state.client_request[i]; ++ } ++ others_request &= ~BCM_POWER_NOCLIENT; ++ ++ global_request = request | others_request; ++ if (global_request != g_state.global_request) { ++ uint32_t actual; ++ ++ /* Send a request to VideoCore */ ++ bcm_mailbox_write(MBOX_CHAN_POWER, ++ global_request << 4); ++ ++ /* Wait for a response during power-up */ ++ if (global_request & ~g_state.global_request) { ++ rc = bcm_mailbox_read(MBOX_CHAN_POWER, ++ &actual); ++ DPRINTK ++ ("bcm_mailbox_read -> %08x, %d\n", ++ actual, rc); ++ actual >>= 4; ++ } else { ++ rc = 0; ++ actual = global_request; ++ } ++ ++ if (rc == 0) { ++ if (actual != global_request) { ++ printk(KERN_ERR ++ "%s: prev global %x, new global %x, actual %x, request %x, others_request %x\n", ++ __func__, ++ g_state.global_request, ++ global_request, actual, request, others_request); ++ /* A failure */ ++ BUG_ON((others_request & actual) ++ != others_request); ++ request &= actual; ++ rc = -EIO; ++ } ++ ++ g_state.global_request = actual; ++ g_state.client_request[handle] = ++ request; ++ } ++ } ++ } ++ up(&g_state.mutex); ++ } else { ++ rc = -EINVAL; ++ } ++ DPRINTK("bcm_power_request -> %d\n", rc); ++ return rc; ++} ++EXPORT_SYMBOL_GPL(bcm_power_request); ++ ++int bcm_power_close(BCM_POWER_HANDLE_T handle) ++{ ++ int rc; ++ ++ DPRINTK("bcm_power_close(%d)\n", handle); ++ ++ rc = bcm_power_request(handle, BCM_POWER_NONE); ++ if (rc == 0) ++ g_state.client_request[handle] = BCM_POWER_NOCLIENT; ++ ++ return rc; ++} ++EXPORT_SYMBOL_GPL(bcm_power_close); ++ ++static int __init bcm_power_init(void) ++{ ++#if defined(BCM_POWER_ALWAYS_ON) ++ BCM_POWER_HANDLE_T always_on_handle; ++#endif ++ int rc = 0; ++ int i; ++ ++ printk(KERN_INFO "bcm_power: Broadcom power driver\n"); ++ ++ for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) ++ g_state.client_request[i] = BCM_POWER_NOCLIENT; ++ ++ sema_init(&g_state.client_mutex, 1); ++ sema_init(&g_state.mutex, 1); ++ ++ g_state.global_request = 0; ++ ++#if defined(BCM_POWER_ALWAYS_ON) ++ if (BCM_POWER_ALWAYS_ON) { ++ bcm_power_open(&always_on_handle); ++ bcm_power_request(always_on_handle, BCM_POWER_ALWAYS_ON); ++ } ++#endif ++ ++ return rc; ++} ++ ++static void __exit bcm_power_exit(void) ++{ ++ bcm_mailbox_write(MBOX_CHAN_POWER, 0); ++} ++ ++arch_initcall(bcm_power_init); /* Initialize early */ ++module_exit(bcm_power_exit); ++ ++MODULE_AUTHOR("Phil Elwell"); ++MODULE_DESCRIPTION("Interface to BCM2708 power management"); ++MODULE_LICENSE("GPL"); +--- /dev/null ++++ b/arch/arm/mach-bcm2708/vc_mem.c +@@ -0,0 +1,467 @@ ++/***************************************************************************** ++* Copyright 2010 - 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/fs.h> ++#include <linux/device.h> ++#include <linux/cdev.h> ++#include <linux/mm.h> ++#include <linux/slab.h> ++#include <linux/proc_fs.h> ++#include <asm/uaccess.h> ++ ++#ifdef CONFIG_ARCH_KONA ++#include <chal/chal_ipc.h> ++#elif CONFIG_ARCH_BCM2708 ++#else ++#include <csp/chal_ipc.h> ++#endif ++ ++#include "mach/vc_mem.h" ++//#include "interface/vchiq_arm/vchiq_connected.h" ++ ++#define DRIVER_NAME "vc-mem" ++ ++// Uncomment to enable debug logging ++//#define ENABLE_DBG ++ ++#if defined(ENABLE_DBG) ++#define LOG_DBG( fmt, ... ) printk( KERN_INFO fmt "\n", ##__VA_ARGS__ ) ++#else ++#define LOG_DBG( fmt, ... ) ++#endif ++#define LOG_ERR( fmt, ... ) printk( KERN_ERR fmt "\n", ##__VA_ARGS__ ) ++ ++// Device (/dev) related variables ++static dev_t vc_mem_devnum = 0; ++static struct class *vc_mem_class = NULL; ++static struct cdev vc_mem_cdev; ++static int vc_mem_inited = 0; ++ ++// Proc entry ++static struct proc_dir_entry *vc_mem_proc_entry; ++ ++/* ++ * Videocore memory addresses and size ++ * ++ * Drivers that wish to know the videocore memory addresses and sizes should ++ * use these variables instead of the MM_IO_BASE and MM_ADDR_IO defines in ++ * headers. This allows the other drivers to not be tied down to a a certain ++ * address/size at compile time. ++ * ++ * In the future, the goal is to have the videocore memory virtual address and ++ * size be calculated at boot time rather than at compile time. The decision of ++ * where the videocore memory resides and its size would be in the hands of the ++ * bootloader (and/or kernel). When that happens, the values of these variables ++ * would be calculated and assigned in the init function. ++ */ ++#ifdef CONFIG_ARCH_KONA ++ ++#include <mach/io_map.h> ++unsigned long mm_vc_mem_phys_addr = VC_EMI; ++ ++#elif CONFIG_ARCH_BCM2708 ++ ++// in the 2835 VC in mapped above ARM, but ARM has full access to VC space ++unsigned long mm_vc_mem_phys_addr = 0x00000000; ++ ++#else ++ ++#include <mach/csp/mm_io.h> ++unsigned long mm_vc_mem_phys_addr = MM_ADDR_IO_VC_EMI; ++ ++#endif ++ ++unsigned int mm_vc_mem_size = 0; ++ ++EXPORT_SYMBOL(mm_vc_mem_phys_addr); ++EXPORT_SYMBOL(mm_vc_mem_size); ++ ++/**************************************************************************** ++* ++* vc_mem_open ++* ++***************************************************************************/ ++ ++static int ++vc_mem_open(struct inode *inode, struct file *file) ++{ ++ (void) inode; ++ (void) file; ++ ++ LOG_DBG("%s: called file = 0x%p", __func__, file); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_release ++* ++***************************************************************************/ ++ ++static int ++vc_mem_release(struct inode *inode, struct file *file) ++{ ++ (void) inode; ++ (void) file; ++ ++ LOG_DBG("%s: called file = 0x%p", __func__, file); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_get_size ++* ++***************************************************************************/ ++ ++static void ++vc_mem_get_size(void) ++{ ++#ifdef CONFIG_ARCH_BCM2708 ++ mm_vc_mem_size = 256 * 1024 * 1024; // Static for now ++#else ++ CHAL_IPC_HANDLE ipc_handle; ++ uint32_t wakeup_register; ++ ++ // Get the videocore memory size from the IPC mailbox if not yet ++ // assigned. ++ if (mm_vc_mem_size == 0) { ++ ipc_handle = chal_ipc_config(NULL); ++ if (ipc_handle == NULL) { ++ LOG_ERR("%s: failed to get IPC handlle", __func__); ++ return; ++ } ++ ++ chal_ipc_query_wakeup_vc(ipc_handle, &wakeup_register); ++ if ((wakeup_register & ~1) == 0) { ++ LOG_DBG("%s: videocore not yet loaded, skipping...", ++ __func__); ++ } else { ++ if (chal_ipc_read_mailbox(ipc_handle, ++ IPC_MAILBOX_ID_0, ++ &mm_vc_mem_size) != ++ BCM_SUCCESS) { ++ LOG_ERR("%s: failed to read from IPC mailbox", ++ __func__); ++ } ++ } ++ } ++#endif ++} ++ ++/**************************************************************************** ++* ++* vc_mem_get_current_size ++* ++***************************************************************************/ ++ ++int ++vc_mem_get_current_size(void) ++{ ++ vc_mem_get_size(); ++ return mm_vc_mem_size; ++} ++ ++EXPORT_SYMBOL_GPL(vc_mem_get_current_size); ++ ++/**************************************************************************** ++* ++* vc_mem_ioctl ++* ++***************************************************************************/ ++ ++static long ++vc_mem_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ int rc = 0; ++ ++ (void) cmd; ++ (void) arg; ++ ++ LOG_DBG("%s: called file = 0x%p", __func__, file); ++ ++ switch (cmd) { ++ case VC_MEM_IOC_MEM_PHYS_ADDR: ++ { ++ LOG_DBG("%s: VC_MEM_IOC_MEM_PHYS_ADDR=0x%p", ++ __func__, (void *) mm_vc_mem_phys_addr); ++ ++ if (copy_to_user((void *) arg, &mm_vc_mem_phys_addr, ++ sizeof (mm_vc_mem_phys_addr)) != 0) { ++ rc = -EFAULT; ++ } ++ break; ++ } ++ case VC_MEM_IOC_MEM_SIZE: ++ { ++ // Get the videocore memory size first ++ vc_mem_get_size(); ++ ++ LOG_DBG("%s: VC_MEM_IOC_MEM_SIZE=%u", __func__, ++ mm_vc_mem_size); ++ ++ if (copy_to_user((void *) arg, &mm_vc_mem_size, ++ sizeof (mm_vc_mem_size)) != 0) { ++ rc = -EFAULT; ++ } ++ break; ++ } ++ default: ++ { ++ return -ENOTTY; ++ } ++ } ++ LOG_DBG("%s: file = 0x%p returning %d", __func__, file, rc); ++ ++ return rc; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_mmap ++* ++***************************************************************************/ ++ ++static int ++vc_mem_mmap(struct file *filp, struct vm_area_struct *vma) ++{ ++ int rc = 0; ++ unsigned long length = vma->vm_end - vma->vm_start; ++ unsigned long offset = vma->vm_pgoff << PAGE_SHIFT; ++ ++ LOG_DBG("%s: vm_start = 0x%08lx vm_end = 0x%08lx vm_pgoff = 0x%08lx", ++ __func__, (long) vma->vm_start, (long) vma->vm_end, ++ (long) vma->vm_pgoff); ++ ++ if (offset + length > mm_vc_mem_size) { ++ LOG_ERR("%s: length %ld is too big", __func__, length); ++ return -EINVAL; ++ } ++ // Do not cache the memory map ++ vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot); ++ ++ rc = remap_pfn_range(vma, vma->vm_start, ++ (mm_vc_mem_phys_addr >> PAGE_SHIFT) + ++ vma->vm_pgoff, length, vma->vm_page_prot); ++ if (rc != 0) { ++ LOG_ERR("%s: remap_pfn_range failed (rc=%d)", __func__, rc); ++ } ++ ++ return rc; ++} ++ ++/**************************************************************************** ++* ++* File Operations for the driver. ++* ++***************************************************************************/ ++ ++static const struct file_operations vc_mem_fops = { ++ .owner = THIS_MODULE, ++ .open = vc_mem_open, ++ .release = vc_mem_release, ++ .unlocked_ioctl = vc_mem_ioctl, ++ .mmap = vc_mem_mmap, ++}; ++ ++/**************************************************************************** ++* ++* vc_mem_proc_read ++* ++***************************************************************************/ ++ ++static int ++vc_mem_proc_read(char *buf, char **start, off_t offset, int count, int *eof, ++ void *data) ++{ ++ char *p = buf; ++ ++ (void) start; ++ (void) count; ++ (void) data; ++ ++ if (offset > 0) { ++ *eof = 1; ++ return 0; ++ } ++ // Get the videocore memory size first ++ vc_mem_get_size(); ++ ++ p += sprintf(p, "Videocore memory:\n"); ++ if (mm_vc_mem_phys_addr != 0) ++ p += sprintf(p, " Physical address: 0x%p\n", ++ (void *) mm_vc_mem_phys_addr); ++ else ++ p += sprintf(p, " Physical address: 0x00000000\n"); ++ p += sprintf(p, " Length (bytes): %u\n", mm_vc_mem_size); ++ ++ *eof = 1; ++ return p - buf; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_proc_write ++* ++***************************************************************************/ ++ ++static int ++vc_mem_proc_write(struct file *file, const char __user * buffer, ++ unsigned long count, void *data) ++{ ++ int rc = -EFAULT; ++ char input_str[10]; ++ ++ memset(input_str, 0, sizeof (input_str)); ++ ++ if (count > sizeof (input_str)) { ++ LOG_ERR("%s: input string length too long", __func__); ++ goto out; ++ } ++ ++ if (copy_from_user(input_str, buffer, count - 1)) { ++ LOG_ERR("%s: failed to get input string", __func__); ++ goto out; ++ } ++ ++ if (strncmp(input_str, "connect", strlen("connect")) == 0) { ++ // Get the videocore memory size from the videocore ++ vc_mem_get_size(); ++ } ++ ++ out: ++ return rc; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_connected_init ++* ++* This function is called once the videocore has been connected. ++* ++***************************************************************************/ ++ ++void ++vc_mem_connected_init(void) ++{ ++ int rc = -EFAULT; ++ struct device *dev; ++ ++ LOG_DBG("%s: called", __func__); ++ ++ vc_mem_get_size(); ++ ++ printk("vc-mem: mm_vc_mem_phys_addr = 0x%08lx\n", mm_vc_mem_phys_addr); ++ printk("vc-mem: mm_vc_mem_size = 0x%08x (%u MiB)\n", ++ mm_vc_mem_size, mm_vc_mem_size / (1024 * 1024)); ++ ++ if ((rc = alloc_chrdev_region(&vc_mem_devnum, 0, 1, DRIVER_NAME)) < 0) { ++ LOG_ERR("%s: alloc_chrdev_region failed (rc=%d)", __func__, rc); ++ goto out_err; ++ } ++ ++ cdev_init(&vc_mem_cdev, &vc_mem_fops); ++ if ((rc = cdev_add(&vc_mem_cdev, vc_mem_devnum, 1)) != 0) { ++ LOG_ERR("%s: cdev_add failed (rc=%d)", __func__, rc); ++ goto out_unregister; ++ } ++ ++ vc_mem_class = class_create(THIS_MODULE, DRIVER_NAME); ++ if (IS_ERR(vc_mem_class)) { ++ rc = PTR_ERR(vc_mem_class); ++ LOG_ERR("%s: class_create failed (rc=%d)", __func__, rc); ++ goto out_cdev_del; ++ } ++ ++ dev = device_create(vc_mem_class, NULL, vc_mem_devnum, NULL, ++ DRIVER_NAME); ++ if (IS_ERR(dev)) { ++ rc = PTR_ERR(dev); ++ LOG_ERR("%s: device_create failed (rc=%d)", __func__, rc); ++ goto out_class_destroy; ++ } ++ ++ vc_mem_proc_entry = create_proc_entry(DRIVER_NAME, 0444, NULL); ++ if (vc_mem_proc_entry == NULL) { ++ rc = -EFAULT; ++ LOG_ERR("%s: create_proc_entry failed", __func__); ++ goto out_device_destroy; ++ } ++ vc_mem_proc_entry->read_proc = vc_mem_proc_read; ++ vc_mem_proc_entry->write_proc = vc_mem_proc_write; ++ ++ vc_mem_inited = 1; ++ return; ++ ++ out_device_destroy: ++ device_destroy(vc_mem_class, vc_mem_devnum); ++ ++ out_class_destroy: ++ class_destroy(vc_mem_class); ++ vc_mem_class = NULL; ++ ++ out_cdev_del: ++ cdev_del(&vc_mem_cdev); ++ ++ out_unregister: ++ unregister_chrdev_region(vc_mem_devnum, 1); ++ ++ out_err: ++ return; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_init ++* ++***************************************************************************/ ++ ++static int __init ++vc_mem_init(void) ++{ ++ printk(KERN_INFO "vc-mem: Videocore memory driver\n"); ++ ++ //vchiq_add_connected_callback(vc_mem_connected_init); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* vc_mem_exit ++* ++***************************************************************************/ ++ ++static void __exit ++vc_mem_exit(void) ++{ ++ LOG_DBG("%s: called", __func__); ++ ++ if (vc_mem_inited) { ++ remove_proc_entry(vc_mem_proc_entry->name, NULL); ++ device_destroy(vc_mem_class, vc_mem_devnum); ++ class_destroy(vc_mem_class); ++ cdev_del(&vc_mem_cdev); ++ unregister_chrdev_region(vc_mem_devnum, 1); ++ } ++} ++ ++module_init(vc_mem_init); ++module_exit(vc_mem_exit); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Broadcom Corporation"); +--- /dev/null ++++ b/arch/arm/mach-bcm2708/vcio.c +@@ -0,0 +1,309 @@ ++/* ++ * linux/arch/arm/mach-bcm2708/vcio.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * 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. ++ * ++ * This device provides a shared mechanism for writing to the mailboxes, ++ * semaphores, doorbells etc. that are shared between the ARM and the ++ * VideoCore processor ++ */ ++ ++#if defined(CONFIG_SERIAL_BCM_MBOX_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) ++#define SUPPORT_SYSRQ ++#endif ++ ++#include <linux/module.h> ++#include <linux/console.h> ++#include <linux/serial_core.h> ++#include <linux/serial.h> ++#include <linux/errno.h> ++#include <linux/device.h> ++#include <linux/init.h> ++#include <linux/mm.h> ++#include <linux/dma-mapping.h> ++#include <linux/platform_device.h> ++#include <linux/sysrq.h> ++#include <linux/delay.h> ++#include <linux/slab.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++ ++#include <linux/io.h> ++ ++#include <mach/vcio.h> ++#include <mach/platform.h> ++ ++#define DRIVER_NAME BCM_VCIO_DRIVER_NAME ++ ++/* ---------------------------------------------------------------------- ++ * Mailbox ++ * -------------------------------------------------------------------- */ ++ ++/* offsets from a mail box base address */ ++#define MAIL_WRT 0x00 /* write - and next 4 words */ ++#define MAIL_RD 0x00 /* read - and next 4 words */ ++#define MAIL_POL 0x10 /* read without popping the fifo */ ++#define MAIL_SND 0x14 /* sender ID (bottom two bits) */ ++#define MAIL_STA 0x18 /* status */ ++#define MAIL_CNF 0x1C /* configuration */ ++ ++#define MBOX_MSG(chan, data28) (((data28) & ~0xf) | ((chan) & 0xf)) ++#define MBOX_MSG_LSB(chan, data28) (((data28) << 4) | ((chan) & 0xf)) ++#define MBOX_CHAN(msg) ((msg) & 0xf) ++#define MBOX_DATA28(msg) ((msg) & ~0xf) ++#define MBOX_DATA28_LSB(msg) (((uint32_t)msg) >> 4) ++ ++#define MBOX_MAGIC 0xd0d0c0de ++ ++struct vc_mailbox { ++ struct device *dev; /* parent device */ ++ void __iomem *status; ++ void __iomem *config; ++ void __iomem *read; ++ void __iomem *write; ++ uint32_t msg[MBOX_CHAN_COUNT]; ++ struct semaphore sema[MBOX_CHAN_COUNT]; ++ uint32_t magic; ++}; ++ ++static void mbox_init(struct vc_mailbox *mbox_out, struct device *dev, ++ uint32_t addr_mbox) ++{ ++ int i; ++ ++ mbox_out->dev = dev; ++ mbox_out->status = __io_address(addr_mbox + MAIL_STA); ++ mbox_out->config = __io_address(addr_mbox + MAIL_CNF); ++ mbox_out->read = __io_address(addr_mbox + MAIL_RD); ++ /* Write to the other mailbox */ ++ mbox_out->write = ++ __io_address((addr_mbox ^ ARM_0_MAIL0_WRT ^ ARM_0_MAIL1_WRT) + ++ MAIL_WRT); ++ ++ for (i = 0; i < MBOX_CHAN_COUNT; i++) { ++ mbox_out->msg[i] = 0; ++ sema_init(&mbox_out->sema[i], 0); ++ } ++ ++ /* Enable the interrupt on data reception */ ++ writel(ARM_MC_IHAVEDATAIRQEN, mbox_out->config); ++ ++ mbox_out->magic = MBOX_MAGIC; ++} ++ ++static int mbox_write(struct vc_mailbox *mbox, unsigned chan, uint32_t data28) ++{ ++ int rc; ++ ++ if (mbox->magic != MBOX_MAGIC) ++ rc = -EINVAL; ++ else { ++ /* wait for the mailbox FIFO to have some space in it */ ++ while (0 != (readl(mbox->status) & ARM_MS_FULL)) ++ cpu_relax(); ++ ++ writel(MBOX_MSG(chan, data28), mbox->write); ++ rc = 0; ++ } ++ return rc; ++} ++ ++static int mbox_read(struct vc_mailbox *mbox, unsigned chan, uint32_t *data28) ++{ ++ int rc; ++ ++ if (mbox->magic != MBOX_MAGIC) ++ rc = -EINVAL; ++ else { ++ if (mbox->msg[chan] || ++ (down_interruptible(&mbox->sema[chan]) == 0)) { ++ *data28 = MBOX_DATA28(mbox->msg[chan]); ++ mbox->msg[chan] = 0; ++ rc = 0; ++ } else { ++ /* The wait was interrupted */ ++ rc = -EINTR; ++ } ++ } ++ return rc; ++} ++ ++static irqreturn_t mbox_irq(int irq, void *dev_id) ++{ ++ /* wait for the mailbox FIFO to have some data in it */ ++ struct vc_mailbox *mbox = (struct vc_mailbox *) dev_id; ++ int status = readl(mbox->status); ++ int ret = IRQ_NONE; ++ ++ while (!(status & ARM_MS_EMPTY)) { ++ uint32_t msg = readl(mbox->read); ++ int chan = MBOX_CHAN(msg); ++ if (chan < MBOX_CHAN_COUNT) { ++ if (mbox->msg[chan]) { ++ /* Overflow */ ++ printk(KERN_ERR DRIVER_NAME ++ ": mbox chan %d overflow - drop %08x\n", ++ chan, msg); ++ } else { ++ mbox->msg[chan] = (msg | 0xf); ++ up(&mbox->sema[chan]); ++ } ++ } else { ++ printk(KERN_ERR DRIVER_NAME ++ ": invalid channel selector (msg %08x)\n", msg); ++ } ++ ret = IRQ_HANDLED; ++ status = readl(mbox->status); ++ } ++ return ret; ++} ++ ++static struct irqaction mbox_irqaction = { ++ .name = "ARM Mailbox IRQ", ++ .flags = IRQF_DISABLED | IRQF_IRQPOLL, ++ .handler = mbox_irq, ++}; ++ ++/* ---------------------------------------------------------------------- ++ * Mailbox Methods ++ * -------------------------------------------------------------------- */ ++ ++static struct device *mbox_dev; /* we assume there's only one! */ ++ ++static int dev_mbox_write(struct device *dev, unsigned chan, uint32_t data28) ++{ ++ int rc; ++ ++ struct vc_mailbox *mailbox = dev_get_drvdata(dev); ++ device_lock(dev); ++ rc = mbox_write(mailbox, chan, data28); ++ device_unlock(dev); ++ ++ return rc; ++} ++ ++static int dev_mbox_read(struct device *dev, unsigned chan, uint32_t *data28) ++{ ++ int rc; ++ ++ struct vc_mailbox *mailbox = dev_get_drvdata(dev); ++ device_lock(dev); ++ rc = mbox_read(mailbox, chan, data28); ++ device_unlock(dev); ++ ++ return rc; ++} ++ ++extern int bcm_mailbox_write(unsigned chan, uint32_t data28) ++{ ++ if (mbox_dev) ++ return dev_mbox_write(mbox_dev, chan, data28); ++ else ++ return -ENODEV; ++} ++EXPORT_SYMBOL_GPL(bcm_mailbox_write); ++ ++extern int bcm_mailbox_read(unsigned chan, uint32_t *data28) ++{ ++ if (mbox_dev) ++ return dev_mbox_read(mbox_dev, chan, data28); ++ else ++ return -ENODEV; ++} ++EXPORT_SYMBOL_GPL(bcm_mailbox_read); ++ ++static void dev_mbox_register(const char *dev_name, struct device *dev) ++{ ++ mbox_dev = dev; ++} ++ ++/* ---------------------------------------------------------------------- ++ * Platform Device for Mailbox ++ * -------------------------------------------------------------------- */ ++ ++static int bcm_vcio_probe(struct platform_device *pdev) ++{ ++ int ret = 0; ++ struct vc_mailbox *mailbox; ++ ++ mailbox = kzalloc(sizeof(*mailbox), GFP_KERNEL); ++ if (NULL == mailbox) { ++ printk(KERN_ERR DRIVER_NAME ": failed to allocate " ++ "mailbox memory\n"); ++ ret = -ENOMEM; ++ } else { ++ struct resource *res; ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (res == NULL) { ++ printk(KERN_ERR DRIVER_NAME ": failed to obtain memory " ++ "resource\n"); ++ ret = -ENODEV; ++ kfree(mailbox); ++ } else { ++ /* should be based on the registers from res really */ ++ mbox_init(mailbox, &pdev->dev, ARM_0_MAIL0_RD); ++ ++ platform_set_drvdata(pdev, mailbox); ++ dev_mbox_register(DRIVER_NAME, &pdev->dev); ++ ++ mbox_irqaction.dev_id = mailbox; ++ setup_irq(IRQ_ARM_MAILBOX, &mbox_irqaction); ++ printk(KERN_INFO DRIVER_NAME ": mailbox at %p\n", ++ __io_address(ARM_0_MAIL0_RD)); ++ } ++ } ++ return ret; ++} ++ ++static int bcm_vcio_remove(struct platform_device *pdev) ++{ ++ struct vc_mailbox *mailbox = platform_get_drvdata(pdev); ++ ++ platform_set_drvdata(pdev, NULL); ++ kfree(mailbox); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm_mbox_driver = { ++ .probe = bcm_vcio_probe, ++ .remove = bcm_vcio_remove, ++ ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init bcm_mbox_init(void) ++{ ++ int ret; ++ ++ printk(KERN_INFO "mailbox: Broadcom VideoCore Mailbox driver\n"); ++ ++ ret = platform_driver_register(&bcm_mbox_driver); ++ if (ret != 0) { ++ printk(KERN_ERR DRIVER_NAME ": failed to register " ++ "on platform\n"); ++ } ++ ++ return ret; ++} ++ ++static void __exit bcm_mbox_exit(void) ++{ ++ platform_driver_unregister(&bcm_mbox_driver); ++} ++ ++arch_initcall(bcm_mbox_init); /* Initialize early */ ++module_exit(bcm_mbox_exit); ++ ++MODULE_AUTHOR("Gray Girling"); ++MODULE_DESCRIPTION("ARM I/O to VideoCore processor"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:bcm-mbox"); +--- a/arch/arm/mm/Kconfig ++++ b/arch/arm/mm/Kconfig +@@ -390,7 +390,7 @@ config CPU_PJ4 + + # ARMv6 + config CPU_V6 +- bool "Support ARM V6 processor" if ARCH_INTEGRATOR || MACH_REALVIEW_EB || MACH_REALVIEW_PBX ++ bool "Support ARM V6 processor" if ARCH_INTEGRATOR || MACH_REALVIEW_EB || MACH_REALVIEW_PBX || MACH_BCM2708 + select CPU_32v6 + select CPU_ABRT_EV6 + select CPU_PABRT_V6 +--- a/arch/arm/mm/alignment.c ++++ b/arch/arm/mm/alignment.c +@@ -855,9 +855,11 @@ do_alignment(unsigned long addr, unsigne + case 0x08000000: /* ldm or stm, or thumb-2 32bit instruction */ + if (thumb2_32b) + handler = do_alignment_t32_to_handler(&instr, regs, &offset); +- else ++ else { ++ offset.un = 0; /* to keep compiler happy */ + handler = do_alignment_ldmstm; +- break; ++ } ++ break; + + default: + goto bad; +--- a/arch/arm/mm/proc-v6.S ++++ b/arch/arm/mm/proc-v6.S +@@ -73,10 +73,19 @@ ENDPROC(cpu_v6_reset) + * + * IRQs are already disabled. + */ ++ ++/* See jira SW-5991 for details of this workaround */ + ENTRY(cpu_v6_do_idle) +- mov r1, #0 +- mcr p15, 0, r1, c7, c10, 4 @ DWB - WFI may enter a low-power mode +- mcr p15, 0, r1, c7, c0, 4 @ wait for interrupt ++ .align 5 ++ mov r1, #2 ++1: subs r1, #1 ++ nop ++ mcreq p15, 0, r1, c7, c10, 4 @ DWB - WFI may enter a low-power mode ++ mcreq p15, 0, r1, c7, c0, 4 @ wait for interrupt ++ nop ++ nop ++ nop ++ bne 1b + mov pc, lr + + ENTRY(cpu_v6_dcache_clean_area) +--- a/drivers/mmc/host/Kconfig ++++ b/drivers/mmc/host/Kconfig +@@ -229,6 +229,27 @@ config MMC_SDHCI_S3C_DMA + + YMMV. + ++config MMC_SDHCI_BCM2708 ++ tristate "SDHCI support on BCM2708" ++ depends on MMC_SDHCI && MACH_BCM2708 ++ select MMC_SDHCI_IO_ACCESSORS ++ help ++ This selects the Secure Digital Host Controller Interface (SDHCI) ++ often referrered to as the eMMC block. ++ ++ If you have a controller with this interface, say Y or M here. ++ ++ If unsure, say N. ++ ++config MMC_SDHCI_BCM2708_DMA ++ bool "DMA support on BCM2708 Arasan controller" ++ depends on MMC_SDHCI_BCM2708 && EXPERIMENTAL ++ help ++ Enable DMA support on the Arasan SDHCI controller in Broadcom 2708 ++ based chips. ++ ++ If unsure, say N. ++ + config MMC_OMAP + tristate "TI OMAP Multimedia Card Interface support" + depends on ARCH_OMAP +@@ -296,6 +317,14 @@ config MMC_ATMELMCI + If unsure, say N. + + endchoice ++config MMC_BCM2708 ++ tristate "BCM2708 Multimedia Card Interface support" ++ help ++ This selects the BCM2708 Multimedia Card Interface driver. If ++ you have a BCM2708 platform with a Multimedia Card ++ slot, say Y or M here. ++ ++ If unsure, say N. + + config MMC_ATMELMCI_DMA + bool "Atmel MCI DMA support" +--- a/drivers/mmc/host/Makefile ++++ b/drivers/mmc/host/Makefile +@@ -14,6 +14,7 @@ obj-$(CONFIG_MMC_SDHCI_PXAV3) += sdhci-p + obj-$(CONFIG_MMC_SDHCI_PXAV2) += sdhci-pxav2.o + obj-$(CONFIG_MMC_SDHCI_S3C) += sdhci-s3c.o + obj-$(CONFIG_MMC_SDHCI_SPEAR) += sdhci-spear.o ++obj-$(CONFIG_MMC_SDHCI_BCM2708) += sdhci-bcm2708.o + obj-$(CONFIG_MMC_WBSD) += wbsd.o + obj-$(CONFIG_MMC_AU1X) += au1xmmc.o + obj-$(CONFIG_MMC_OMAP) += omap.o +@@ -53,6 +54,8 @@ obj-$(CONFIG_MMC_SDHCI_TEGRA) += sdhci- + obj-$(CONFIG_MMC_SDHCI_OF_ESDHC) += sdhci-of-esdhc.o + obj-$(CONFIG_MMC_SDHCI_OF_HLWD) += sdhci-of-hlwd.o + ++obj-$(CONFIG_MMC_BCM2708) += bcm2708_mci.o ++ + ifeq ($(CONFIG_CB710_DEBUG),y) + CFLAGS-cb710-mmc += -DDEBUG + endif +--- /dev/null ++++ b/drivers/mmc/host/bcm2708_mci.c +@@ -0,0 +1,889 @@ ++/* ++ * linux/drivers/mmc/host/bcm2708_mci.c - Broadcom BCM2708 MCI driver ++ * ++ * Copyright (C) 2010 Broadcom, All Rights Reserved. ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ */ ++ ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/init.h> ++#include <linux/ioport.h> ++#include <linux/device.h> ++#include <linux/interrupt.h> ++#include <linux/delay.h> ++#include <linux/err.h> ++#include <linux/highmem.h> ++#include <linux/log2.h> ++#include <linux/mmc/host.h> ++#include <linux/mmc/mmc.h> ++#include <linux/mmc/sd.h> ++#include <linux/platform_device.h> ++#include <linux/clk.h> ++#include <linux/scatterlist.h> ++#include <linux/dma-mapping.h> ++ ++#include <asm/cacheflush.h> ++#include <asm/div64.h> ++#include <asm/io.h> ++#include <asm/sizes.h> ++//#include <asm/mach/mmc.h> ++ ++#include <mach/gpio.h> ++ ++#include "bcm2708_mci.h" ++ ++#define DRIVER_NAME "bcm2708_mci" ++ ++//#define PIO_DEBUG ++#ifdef PIO_DEBUG ++#define DBG(host,fmt,args...) \ ++ printk(KERN_ERR"%s: %s: " fmt, mmc_hostname(host->mmc), __func__ , args) ++#else ++#define DBG(host,fmt,args...) \ ++ pr_debug("%s: %s: " fmt, mmc_hostname(host->mmc), __func__ , args) ++#endif ++ ++#define USE_DMA ++#define USE_DMA_IRQ ++ ++#ifdef USE_DMA ++#define SDHOST_DMA_CHANNEL 5 ++#endif ++ ++#define BCM2708_DMA_ACTIVE (1 << 0) ++#define BCM2708_DMA_INT (1 << 2) ++ ++#define BCM2708_DMA_INT_EN (1 << 0) ++#define BCM2708_DMA_D_INC (1 << 4) ++#define BCM2708_DMA_D_WIDTH (1 << 5) ++#define BCM2708_DMA_D_DREQ (1 << 6) ++#define BCM2708_DMA_S_INC (1 << 8) ++#define BCM2708_DMA_S_WIDTH (1 << 9) ++#define BCM2708_DMA_S_DREQ (1 << 10) ++ ++#define BCM2708_DMA_PER_MAP(x) ((x) << 16) ++ ++#define BCM2708_DMA_DREQ_SDHOST 13 ++ ++#define BCM2708_DMA_CS 0x00 ++#define BCM2708_DMA_ADDR 0x04 ++ ++static void dump_sd_regs(void * mmc_base ); ++static int bcm2708_mci_reset(struct bcm2708_mci_host *host); ++ ++static void do_command(void __iomem *base, u32 c, u32 a) ++{ ++ u32 cmdsts = 0; ++ writel(a, base + BCM2708_MCI_ARGUMENT); ++ writel(c | BCM2708_MCI_ENABLE, base + BCM2708_MCI_COMMAND); ++ ++ /* check for error and command done */ ++ cmdsts = readl(base + BCM2708_MCI_COMMAND); ++ while ((cmdsts & BCM2708_MCI_ENABLE) && (!(cmdsts & BCM2708_MCI_FAIL_FLAG))) ++ cmdsts = readl(base + BCM2708_MCI_COMMAND); ++ if (cmdsts & BCM2708_MCI_FAIL_FLAG) { ++ printk(KERN_DEBUG"%s: Command %d failed with arg %d\n", __func__, c, a); ++ dump_sd_regs(base); ++ } ++} ++ ++//static void discard_words(void __iomem *base, int words) ++//{ ++// int i; ++// for (i = 0; i < words; i++) { ++// while (!(readl(base + BCM2708_MCI_STATUS) & BCM2708_MCI_DATAFLAG)); ++// readl(base + BCM2708_MCI_DATA); ++// } ++//} ++ ++#define CACHE_LINE_MASK 31 ++ ++static int suitable_for_dma(struct scatterlist *sg_ptr, int sg_len) ++{ ++ int i; ++ ++ for (i = 0; i < sg_len; i++) { ++ if (sg_ptr[i].offset & CACHE_LINE_MASK || sg_ptr[i].length & CACHE_LINE_MASK) ++ return 0; ++ } ++ ++ return 1; ++} ++ ++static void wait_for_complete(struct bcm2708_mci_host *host, ++ void __iomem *mmc_base) ++{ ++#ifdef USE_SDHOST_IRQ ++#error not implemented yet ++#else ++ while ((readl(mmc_base + BCM2708_MCI_STATUS) & ++ (BCM2708_MCI_HSTS_BUSY | BCM2708_MCI_HSTS_BLOCK)) == 0) ++ continue; ++ ++ writel(BCM2708_MCI_HSTS_BUSY | BCM2708_MCI_HSTS_BLOCK, ++ mmc_base + BCM2708_MCI_STATUS); ++#endif ++} ++ ++static void dump_sd_regs(void * mmc_base ) ++{ ++ printk(KERN_DEBUG"Registers:\n"); ++ printk(KERN_DEBUG"SDCMD:0x%x\n", readl(mmc_base + BCM2708_MCI_COMMAND)); ++ printk(KERN_DEBUG"SDARG:0x%x\n", readl(mmc_base + BCM2708_MCI_ARGUMENT)); ++ printk(KERN_DEBUG"SDTOUT:0x%x\n", readl(mmc_base + BCM2708_MCI_TIMEOUT)); ++ printk(KERN_DEBUG"SDCDIV:0x%x\n", readl(mmc_base + BCM2708_MCI_CLKDIV)); ++ printk(KERN_DEBUG"SDRSP0:0x%x\n", readl(mmc_base + BCM2708_MCI_RESPONSE0)); ++ printk(KERN_DEBUG"SDRSP1:0x%x\n", readl(mmc_base + BCM2708_MCI_RESPONSE1)); ++ printk(KERN_DEBUG"SDRSP2:0x%x\n", readl(mmc_base + BCM2708_MCI_RESPONSE2)); ++ printk(KERN_DEBUG"SDRSP3:0x%x\n", readl(mmc_base + BCM2708_MCI_RESPONSE3)); ++ printk(KERN_DEBUG"SDHSTS:0x%x\n", readl(mmc_base + BCM2708_MCI_STATUS)); ++ printk(KERN_DEBUG"SDPO:0x%x\n", readl(mmc_base + BCM2708_MCI_VDD)); ++ printk(KERN_DEBUG"SDEDM:0x%x\n", readl(mmc_base + BCM2708_MCI_EDM)); ++ printk(KERN_DEBUG"SDHCFG:0x%x\n", readl(mmc_base + BCM2708_MCI_HOSTCONFIG)); ++ printk(KERN_DEBUG"SDHBCT:0x%x\n", readl(mmc_base + BCM2708_MCI_HBCT)); ++ //printk(KERN_ERR"SDDATA:0x%x\n", readl(mmc_base + BCM2708_MCI_DATA)); ++ printk(KERN_DEBUG"SDHBLC:0x%x\n", readl(mmc_base + BCM2708_MCI_HBLC)); ++} ++ ++ ++static void ++bcm2708_mci_start_command(struct bcm2708_mci_host *host, struct mmc_command *cmd, struct mmc_data *data) ++{ ++ void __iomem *mmc_base = host->mmc_base; ++ void __iomem *dma_base = host->dma_base; ++ u32 status; ++ u32 c; ++ int redo = 0; ++ ++ DBG(host, "op %02x arg %08x flags %08x\n", ++ cmd->opcode, cmd->arg, cmd->flags); ++ ++back: ++ ++ /* ++ * clear the controller status register ++ */ ++ ++ writel(-1, mmc_base + BCM2708_MCI_STATUS); ++ ++ /* ++ * build the command register write, incorporating no ++ * response, long response, busy, read and write flags ++ */ ++ ++ c = cmd->opcode; ++ if (cmd->flags & MMC_RSP_PRESENT) { ++ if (cmd->flags & MMC_RSP_136) ++ c |= BCM2708_MCI_LONGRESP; ++ } else ++ c |= BCM2708_MCI_NORESP; ++ if (cmd->flags & MMC_RSP_BUSY) ++ c |= BCM2708_MCI_BUSY; ++ ++ if (data) { ++ if (data->flags & MMC_DATA_READ) ++ c |= BCM2708_MCI_READ; ++ else ++ c |= BCM2708_MCI_WRITE; ++ ++ DBG(host, "BYTECOUT %d BLOCKCOUNT %d .. ",readl(mmc_base + BCM2708_MCI_HBCT), readl(mmc_base + BCM2708_MCI_HBLC)); ++ DBG(host, "set blocksize to %d\n", data->blksz); ++ DBG(host, "set blockcnt to %d\n", data->blocks); ++ writel( data->blksz, mmc_base + BCM2708_MCI_HBCT); ++ writel(data->blocks, mmc_base + BCM2708_MCI_HBLC); ++ } ++ ++ /* ++ * run the command and wait for it to complete ++ */ ++ ++ DBG(host, "executing command=%d\n", cmd->opcode); ++ ++ do_command(mmc_base, c, cmd->arg); ++ ++ DBG(host, "done cmd=%d\n", cmd->opcode); ++ ++ if (c & BCM2708_MCI_BUSY) { ++ ++ DBG(host, "waiting for command(%d) to complete\n", cmd->opcode); ++ wait_for_complete(host, mmc_base); ++ DBG(host, "done waiting for command(%d)\n", cmd->opcode); ++ } ++ ++ /* ++ * retrieve the response and error (if any) ++ */ ++ ++ status = readl(mmc_base + BCM2708_MCI_STATUS); ++ ++ if (cmd->flags & MMC_RSP_136) { ++ cmd->resp[3] = readl(mmc_base + BCM2708_MCI_RESPONSE0); ++ cmd->resp[2] = readl(mmc_base + BCM2708_MCI_RESPONSE1); ++ cmd->resp[1] = readl(mmc_base + BCM2708_MCI_RESPONSE2); ++ cmd->resp[0] = readl(mmc_base + BCM2708_MCI_RESPONSE3); ++ } else { ++ cmd->resp[0] = readl(mmc_base + BCM2708_MCI_RESPONSE0); ++ } ++ ++ if (status & BCM2708_MCI_CMDTIMEOUT) { ++ printk(KERN_DEBUG "mmc driver saw timeout with opcode = %d, data = 0x%08x, timeout = %d", cmd->opcode, (unsigned int)data, readl(mmc_base + BCM2708_MCI_TIMEOUT)); ++ if (data) ++ printk(KERN_DEBUG " data->sg_len = %d\n", data->sg_len); ++ else ++ printk(KERN_DEBUG "\n"); ++ if (!redo) { ++ printk(KERN_DEBUG "redo\n"); ++ redo = 1; ++ goto back; ++ } else ++ cmd->error = -ETIMEDOUT; ++ } ++ ++ /* ++ * pump data if necessary ++ */ ++ ++ if (data) { ++ unsigned int sg_len = data->sg_len; ++ struct scatterlist *sg_ptr = data->sg; ++ ++ data->bytes_xfered = 0; ++ ++#ifdef USE_DMA ++ if (suitable_for_dma(sg_ptr, sg_len)) { ++ int i, count = dma_map_sg(&host->dev->dev, sg_ptr, sg_len, data->flags & MMC_DATA_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE); ++ ++ for (i = 0; i < count; i++) { ++ BCM2708_DMA_CB_T *cb = &host->cb_base[i]; ++ ++ if (data->flags & MMC_DATA_READ) { ++ cb->info = BCM2708_DMA_PER_MAP(BCM2708_DMA_DREQ_SDHOST)|BCM2708_DMA_S_DREQ|BCM2708_DMA_D_WIDTH|BCM2708_DMA_D_INC; ++ cb->src = 0x7e202040; ++ cb->dst = sg_dma_address(&sg_ptr[i]); ++ } else { ++ cb->info = BCM2708_DMA_PER_MAP(BCM2708_DMA_DREQ_SDHOST)|BCM2708_DMA_S_WIDTH|BCM2708_DMA_S_INC|BCM2708_DMA_D_DREQ; ++ cb->src = sg_dma_address(&sg_ptr[i]); ++ cb->dst = 0x7e202040; ++ } ++ ++ cb->length = sg_dma_len(&sg_ptr[i]); ++ cb->stride = 0; ++ ++ if (i == count - 1) { ++#ifdef USE_DMA_IRQ ++ cb->info |= BCM2708_DMA_INT_EN; ++#endif ++ cb->next = 0; ++ } else ++ cb->next = host->cb_handle + (i + 1) * sizeof(BCM2708_DMA_CB_T); ++ ++ cb->pad[0] = 0; ++ cb->pad[1] = 0; ++ ++ data->bytes_xfered += sg_ptr[i].length; ++ } ++ ++ dsb(); // data barrier operation ++ ++ writel(host->cb_handle, dma_base + BCM2708_DMA_ADDR); ++ writel(BCM2708_DMA_ACTIVE, dma_base + BCM2708_DMA_CS); ++ ++#ifdef USE_DMA_IRQ ++ down(&host->sem); ++#else ++ while ((readl(dma_base + BCM2708_DMA_CS) & BCM2708_DMA_ACTIVE)); ++#endif ++ dma_unmap_sg(&host->dev->dev, sg_ptr, sg_len, data->flags & MMC_DATA_READ ? DMA_FROM_DEVICE : DMA_TO_DEVICE); ++ } else ++#endif ++ while (sg_len) { ++ unsigned long flags; ++ char *buffer; ++ u32 *ptr, *lim; ++ ++ DBG(host, "sg_len=%d sg_ptr=%p len=%d\n", sg_len, sg_ptr, sg_ptr->length); ++ ++ /* ++ * map the current scatter buffer ++ */ ++ ++ buffer = bcm2708_mci_kmap_atomic(sg_ptr, &flags); ++ ++ /* ++ * pump the data ++ */ ++ ++ ptr = (u32 *)(buffer); ++ lim = (u32 *)(buffer + sg_ptr->length); ++ ++ while (ptr < lim) ++ { ++#ifdef PIO_DEBUG ++ unsigned int wait_count = 1; ++#endif ++ while (!(readl(mmc_base + BCM2708_MCI_STATUS) & BCM2708_MCI_DATAFLAG)) ++ { ++#ifdef PIO_DEBUG ++ wait_count++; ++ if ( 0 == (wait_count % 20000) ) { ++ ++ printk(KERN_ERR"Timeout waiting for data flag\n"); ++ dump_sd_regs(mmc_base); ++ } ++#endif ++ } ++ ++ if (data->flags & MMC_DATA_READ) ++ *ptr++ = readl(mmc_base + BCM2708_MCI_DATA); ++ else ++ { ++#ifdef PIO_DEBUG ++ uint32_t fifo_bytes, fifo_wait_count = 1; ++ ++ fifo_bytes = readl(mmc_base + BCM2708_MCI_EDM); ++ fifo_bytes = (fifo_bytes >> 4) & 0xf; ++ ++ while(fifo_bytes > 3) ++ { ++ fifo_wait_count++; ++ if ( 0 == (fifo_wait_count % 20000) ) { ++ printk(KERN_ERR"waiting for fifo_bytes < 3\n"); ++ dump_sd_regs(mmc_base); ++ } ++ ++ fifo_bytes = readl(mmc_base + BCM2708_MCI_EDM); ++ fifo_bytes = (fifo_bytes >> 4) & 0xf; ++ } ++ ++ BUG_ON(fifo_bytes > 3); ++#endif ++ writel(*ptr++, mmc_base + BCM2708_MCI_DATA); ++ } ++ } ++ ++ DBG(host, "done reading/writing %d bytes from mmc\n", sg_ptr->length); ++ ++ ++ /* ++ * unmap the buffer ++ */ ++ ++ bcm2708_mci_kunmap_atomic(buffer, &flags); ++ ++ /* ++ * if we were reading, and we have completed this ++ * page, ensure that the data cache is coherent ++ */ ++ ++ if (data->flags & MMC_DATA_READ) ++ flush_dcache_page(sg_page(sg_ptr)); ++ ++ data->bytes_xfered += sg_ptr->length; ++ ++ sg_ptr++; ++ sg_len--; ++ } ++ ++// if (host->is_acmd && cmd->opcode == SD_APP_SEND_SCR) ++// discard_words(mmc_base, 126); ++// if (host->is_acmd && cmd->opcode == SD_APP_SEND_NUM_WR_BLKS) ++// discard_words(mmc_base, 127); ++// if (!host->is_acmd && cmd->opcode == SD_SWITCH) ++// discard_words(mmc_base, 112); ++ ++ if (data->stop) { ++ ++ DBG(host, "sending stop command %p\n", data->stop); ++ bcm2708_mci_start_command(host, data->stop, 0); ++ ++ while ((readl(mmc_base + BCM2708_MCI_STATUS) & ++ BCM2708_MCI_DATAFLAG)) ++ { ++ DBG(host, "error data flag still set read=%d bytes\n", sg_ptr->length); ++ printk(KERN_ERR"SDDATA:0x%x\n", readl(mmc_base + BCM2708_MCI_DATA)); ++ dump_sd_regs(mmc_base); ++ } ++ } ++ } ++ /* ++ * remember if we're an application command ++ */ ++ host->is_acmd = cmd->opcode == MMC_APP_CMD; ++} ++ ++static void bcm2708_mci_request(struct mmc_host *mmc, struct mmc_request *mrq) ++{ ++ struct bcm2708_mci_host *host = mmc_priv(mmc); ++ ++ if (mrq->data && !is_power_of_2(mrq->data->blksz)) { ++ printk(KERN_ERR "%s: Unsupported block size (%d bytes)\n", ++ mmc_hostname(mmc), mrq->data->blksz); ++ mrq->cmd->error = -EINVAL; ++ mmc_request_done(mmc, mrq); ++ return; ++ } ++ ++ bcm2708_mci_start_command(host, mrq->cmd, mrq->data); ++ ++ mmc_request_done(host->mmc, mrq); ++} ++ ++static void bcm2708_mci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) ++{ ++ ++ struct bcm2708_mci_host *host = mmc_priv(mmc); ++ void *mmc_base = host->mmc_base; ++ ++ ++ printk(KERN_DEBUG"%s: Want to set clock: %d width: %d\n", mmc_hostname(mmc), ++ ios->clock, ios->bus_width); ++ ++ if (ios->clock == 25000000 || ios->clock == 26000000) { ++ printk(KERN_DEBUG"%s setting clock div to 10 (8+2)\n", mmc_hostname(mmc)); ++ writel(0x8, mmc_base + BCM2708_MCI_CLKDIV); ++ } else if (ios->clock == 50000000 || ios->clock == 52000000) { ++ printk(KERN_DEBUG"%s setting clock div to 5 (3+2)\n", mmc_hostname(mmc)); ++ writel(0x3, mmc_base + BCM2708_MCI_CLKDIV); ++ } else { ++ // On init or unknown clock, we set the clock really low ++ printk(KERN_DEBUG"%s Setting clock div to 0x4e0\n", mmc_hostname(mmc)); ++ writel(0x4e0, mmc_base + BCM2708_MCI_CLKDIV); ++ } ++ ++ if (ios->bus_width) { ++ uint32_t hcfg; ++ hcfg = readl(mmc_base + BCM2708_MCI_HOSTCONFIG); ++ printk(KERN_DEBUG"%s setting bus width to %d\n", mmc_hostname(mmc), ios->bus_width); ++ ++ hcfg &= BCM2708_MCI_HOSTCONFIG_WIDEEXT_CLR; ++ hcfg |= (ios->bus_width == MMC_BUS_WIDTH_4) ? BCM2708_MCI_HOSTCONFIG_WIDEEXT_4BIT : 0; ++ ++ writel(hcfg, mmc_base + BCM2708_MCI_HOSTCONFIG); ++ } ++} ++ ++static int bcm2708_mci_get_cd(struct mmc_host *mmc) ++{ ++ int present = -ENOSYS; ++ ++ struct bcm2708_mci_host *host = mmc_priv(mmc); ++ void *gpio_base = host->gpio_base; ++ ++ present = readl( (gpio_base + GP_LEV0) ); ++ ++ if ((present & (1<<29))==(1<<29)) ++ present = 0; ++ else ++ present = 1; ++ ++ printk(KERN_DEBUG"***sdcard present***=%d\n", present); ++ ++ // FIXME - For now force SD card present for 2835DK ++ present = 1; ++ return present; ++} ++ ++/* ++ * Handle completion of command and data transfers. ++ */ ++ ++//static irqreturn_t bcm2708_mci_command_irq(int irq, void *dev_id) ++//{ ++// struct bcm2708_mci_host *host = dev_id; ++// ++// writel(BCM2708_DMA_INT, host->dma_base + BCM2708_DMA_CS); ++// ++// printk(KERN_ERR "irq\n"); ++// ++// return IRQ_RETVAL(0); ++//} ++ ++static irqreturn_t bcm2708_mci_sddet_irq(int irq, void *dev_id) ++{ ++ struct bcm2708_mci_host *host = dev_id; ++ irqreturn_t handled = IRQ_NONE; ++ int present; ++ ++ present = bcm2708_mci_get_cd(host->mmc); ++ ++ if (present!=host->present) ++ { ++ host->present = present; ++ printk(KERN_DEBUG "SDDET IRQ: sdcard present: %d\n",present); ++ bcm2708_mci_reset(host); ++ mmc_detect_change(host->mmc, msecs_to_jiffies(500)); ++ } ++ ++ return IRQ_RETVAL(handled); ++} ++ ++#ifdef USE_DMA_IRQ ++static irqreturn_t bcm2708_mci_data_irq(int irq, void *dev_id) ++{ ++ struct bcm2708_mci_host *host = dev_id; ++ irqreturn_t handled = IRQ_NONE; ++ ++ if (0 != (BCM2708_DMA_INT & readl(host->dma_base + BCM2708_DMA_CS))) { ++ writel(BCM2708_DMA_INT, host->dma_base + BCM2708_DMA_CS); ++ dsb(); ++ handled = IRQ_HANDLED; ++ up(&host->sem); ++ } else { ++ printk(KERN_ERR"bcm2708_mci irq check failed !!\n"); ++ } ++ ++ return IRQ_RETVAL(handled); ++} ++#endif ++ ++static const struct mmc_host_ops bcm2708_mci_ops = { ++ .request = bcm2708_mci_request, ++ .set_ios = bcm2708_mci_set_ios, ++ .get_cd = bcm2708_mci_get_cd, ++}; ++ ++static int bcm2708_mci_reset(struct bcm2708_mci_host *host) ++{ ++ ++ void *mmc_base = host->mmc_base; ++ ++ // pin muxing/gpios is done by vcloader ++ ++ printk(KERN_DEBUG"%s:Resetting BCM2708 MCI Controller.\n", __func__ ); ++ ++ writel(0, mmc_base + BCM2708_MCI_COMMAND); ++ writel(0, mmc_base + BCM2708_MCI_ARGUMENT); ++ writel(0x00F00000, mmc_base + BCM2708_MCI_TIMEOUT); ++ writel(0, mmc_base + BCM2708_MCI_CLKDIV); ++ writel(0, mmc_base + BCM2708_MCI_STATUS); ++ writel(0, mmc_base + BCM2708_MCI_VDD); ++ writel(0, mmc_base + BCM2708_MCI_HOSTCONFIG); ++ writel(0, mmc_base + BCM2708_MCI_HBCT); ++ writel(0, mmc_base + BCM2708_MCI_HBLC); ++ ++ writel( BCM2708_MCI_HOSTCONFIG_SLOW_CARD | BCM2708_MCI_HOSTCONFIG_BUSY_IRPT_EN | ++ BCM2708_MCI_HOSTCONFIG_BLOCK_IRPT_EN | BCM2708_MCI_HOSTCONFIG_WIDE_INT_BUS, ++ mmc_base + BCM2708_MCI_HOSTCONFIG); ++ ++ // On A0 silicon it has been observed that the following must hold ++ // WRITE_THRESHOLD<=5 and READ_THRESHOLD<=WRITE_THRESHOLD+1 ++ // with the chip running at 150MHz (with the interface running @ 150/22 = 6.8 MHz) ++ // the second requirement suggests that the verilog does not properly separate the read / write FIFOs ++ // On V3XDS Read=2 & Write=6 ++ ++#define READ_THRESHOLD 3 ++#define WRITE_THRESHOLD 3 ++#if 1 // !!! This is still required, without it we get CRC16 errors in data. ++ { ++ uint32_t temp; ++ temp = readl(mmc_base + BCM2708_MCI_EDM); ++ temp &= ~((0x1F<<14) | (0x1F<<9)); ++ temp |= (WRITE_THRESHOLD << 9) | (READ_THRESHOLD << 14); ++ writel(temp, mmc_base + BCM2708_MCI_EDM); ++ } ++#endif ++ ++ // Power on delay ++ mdelay(10); ++ writel(BCM2708_MCI_VDD_ENABLE, mmc_base + BCM2708_MCI_VDD); ++ mdelay(10); ++ ++ return 0; ++} ++ ++ ++static int __devinit bcm2708_mci_probe(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc; ++ struct bcm2708_mci_host *host; ++ struct resource *mmc_res; ++ struct resource *dma_res; ++ struct resource *gpio_res; ++ struct resource *dat_res; ++ struct resource *sddet_res; ++ int ret; ++ ++ mmc = mmc_alloc_host(sizeof(struct bcm2708_mci_host), &pdev->dev); ++ if (!mmc) { ++ ret = -ENOMEM; ++ dev_dbg(&pdev->dev, "couldn't allocate mmc host\n"); ++ goto fail0; ++ } ++ ++ host = mmc_priv(mmc); ++ host->mmc = mmc; ++ ++ host->dev = pdev; ++ ++ sema_init(&host->sem, 0); ++ ++#ifdef USE_DMA ++ host->cb_base = dma_alloc_writecombine(&pdev->dev, SZ_4K, &host->cb_handle, GFP_KERNEL); ++ if (!host->cb_base) { ++ ret = -ENOMEM; ++ dev_dbg(&pdev->dev, "couldn't allocate dma base\n"); ++ goto fail1; ++ } ++#endif ++ ++ mmc_res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!mmc_res) { ++ ret = -ENXIO; ++ dev_dbg(&pdev->dev, "couldn't allocate mmc memory resource 0\n"); ++ goto fail2; ++ } ++ ++ if (!request_mem_region(mmc_res->start, mmc_res->end - mmc_res->start + 1, DRIVER_NAME)) { ++ ret = -EBUSY; ++ goto fail2; ++ } ++ ++ dma_res = platform_get_resource(pdev, IORESOURCE_MEM, 1); ++ if (!dma_res) { ++ ret = -ENXIO; ++ dev_dbg(&pdev->dev, "couldn't allocate dma memory resource 1\n"); ++ goto fail3; ++ } ++ ++ /* ++ * Map I/O regions ++ */ ++ ++ host->mmc_base = ioremap(mmc_res->start, resource_size(mmc_res)); ++ if (!host->mmc_base) { ++ ret = -ENOMEM; ++ goto fail3; ++ } ++ ++ gpio_res = platform_get_resource(pdev, IORESOURCE_MEM, 2); ++ if (!gpio_res) { ++ ret = -ENXIO; ++ dev_dbg(&pdev->dev, "couldn't allocate gpio resource\n"); ++ goto fail4; ++ } ++ ++ /* ++ * Map I/O regions ++ */ ++ ++ host->gpio_base = ioremap(gpio_res->start, resource_size(gpio_res)); ++ if (!host->gpio_base) { ++ ret = -ENOMEM; ++ goto fail4; ++ } ++ ++#ifdef USE_DMA ++ host->dma_base = __io_address(dma_res->start); ++ ++ if (!host->dma_base) { ++ ret = -ENOMEM; ++ goto fail5; ++ } ++ ++ // USE DMA5 channel ++ host->dma_base = (void __iomem *)((char *) host->dma_base + (SDHOST_DMA_CHANNEL * 0x100)); ++ ++ dev_dbg(&pdev->dev, "%s: using dma channel %d for sdhost\n", __func__, SDHOST_DMA_CHANNEL); ++ ++ /* ++ * Grab interrupts. ++ */ ++#ifdef USE_DMA_IRQ ++ dat_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ if (!dat_res) { ++ ret = -ENXIO; ++ dev_dbg(&pdev->dev, "couldn't allocate irq for dma\n"); ++ goto fail5; ++ } ++ ++ ret = request_irq(dat_res->start, bcm2708_mci_data_irq, 0, DRIVER_NAME " (dat)", host); ++ if (ret) { ++ goto fail5; ++ } ++ dev_dbg(&pdev->dev, "%s: using dma interrupt number %d for sdhost\n", __func__, dat_res->start); ++ ++#endif ++#endif ++ ++ host->present = bcm2708_mci_get_cd(host->mmc); ++ ++ sddet_res = platform_get_resource(pdev, IORESOURCE_IRQ, 1); ++ if (!sddet_res) { ++ ret = -ENXIO; ++ dev_dbg(&pdev->dev, "couldn't allocate irq for sd detect\n"); ++ goto fail6; ++ } ++ ++ ret = request_irq(sddet_res->start, bcm2708_mci_sddet_irq, 0, DRIVER_NAME " (cmd)", host); ++ if (ret) { ++ goto fail6; ++ } ++ ++ host->is_acmd = 0; ++ ++ mmc->ops = &bcm2708_mci_ops; ++ mmc->f_min = 200000; ++ mmc->f_max = 52000000; ++ mmc->ocr_avail = MMC_VDD_32_33 | MMC_VDD_33_34; ++ ++ /* ++ * We can do SGIO ++ */ ++ mmc->max_segs = NR_SG; ++ ++ /* ++ * Since we only have a 16-bit data length register, we must ++ * ensure that we don't exceed 2^16-1 bytes in a single request. ++ */ ++ mmc->max_req_size = 65535; ++ ++ /* ++ * Set the maximum segment size. Since we aren't doing DMA ++ * (yet) we are only limited by the data length register. ++ */ ++ mmc->max_seg_size = mmc->max_req_size; ++ ++ /* ++ * Block size can be up to 2048 bytes, but must be a power of two. ++ */ ++ mmc->max_blk_size = 2048; ++ ++ /* ++ * No limit on the number of blocks transferred. ++ */ ++ mmc->max_blk_count = mmc->max_req_size; ++ ++ /* ++ * We support 4-bit data (at least on the DB) ++ */ ++ ++ mmc->caps |= (MMC_CAP_4_BIT_DATA | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED) ; ++ ++ bcm2708_mci_reset(host); ++ ++ mmc_add_host(mmc); ++ ++ printk(KERN_INFO "%s: BCM2708 SD host at 0x%08llx 0x%08llx\n", ++ mmc_hostname(mmc), ++ (unsigned long long)mmc_res->start, (unsigned long long)dma_res->start); ++ ++ return 0; ++ ++fail6: ++#ifdef USE_DMA_IRQ ++ free_irq(dat_res->start, host); ++#endif ++fail5: ++ iounmap(host->gpio_base); ++fail4: ++ iounmap(host->mmc_base); ++fail3: ++ release_mem_region(mmc_res->start, mmc_res->end - mmc_res->start + 1); ++fail2: ++ dma_free_writecombine(&pdev->dev, SZ_4K, host->cb_base, host->cb_handle); ++fail1: ++ mmc_free_host(mmc); ++fail0: ++ dev_err(&pdev->dev, "probe failed, err %d\n", ret); ++ return ret; ++} ++ ++static int __devexit bcm2708_mci_remove(struct platform_device *pdev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(pdev); ++ ++ if (mmc) { ++ struct bcm2708_mci_host *host = mmc_priv(mmc); ++ struct resource *res; ++ struct resource *res2; ++ ++ mmc_remove_host(mmc); ++#ifdef USE_DMA ++#ifdef USE_DMA_IRQ ++ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); ++ free_irq(res->start, host); ++#endif ++#endif ++ ++ res2 = platform_get_resource(pdev, IORESOURCE_IRQ, 1); ++ free_irq(res2->start, host); ++ ++ iounmap(host->mmc_base); ++ iounmap(host->gpio_base); ++ iounmap(host->dma_base); ++ ++ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ release_mem_region(res->start, resource_size(res)); ++#ifdef USE_DMA ++ dma_free_writecombine(&pdev->dev, SZ_4K, host->cb_base, host->cb_handle); ++#endif ++ ++ mmc_free_host(mmc); ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++ } else ++ return -1; ++} ++ ++#ifdef CONFIG_PM ++static int bcm2708_mci_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(dev); ++ int ret = 0; ++ ++ if (mmc) { ++ ret = mmc_suspend_host(mmc); ++ } ++ ++ return ret; ++} ++ ++static int bcm2708_mci_resume(struct platform_device *dev) ++{ ++ struct mmc_host *mmc = platform_get_drvdata(dev); ++ int ret = 0; ++ ++ if (mmc) { ++ ret = mmc_resume_host(mmc); ++ } ++ ++ return ret; ++} ++#else ++#define bcm2708_mci_suspend NULL ++#define bcm2708_mci_resume NULL ++#endif ++ ++static struct platform_driver bcm2708_mci_driver = { ++ .probe = bcm2708_mci_probe, ++ .remove = bcm2708_mci_remove, ++ .suspend = bcm2708_mci_suspend, ++ .resume = bcm2708_mci_resume, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init bcm2708_mci_init(void) ++{ ++ return platform_driver_register(&bcm2708_mci_driver); ++} ++ ++static void __exit bcm2708_mci_exit(void) ++{ ++ platform_driver_unregister(&bcm2708_mci_driver); ++} ++ ++module_init(bcm2708_mci_init); ++module_exit(bcm2708_mci_exit); ++ ++MODULE_DESCRIPTION("BCM2708 Multimedia Card Interface driver"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:bcm2708_mci"); +--- /dev/null ++++ b/drivers/mmc/host/bcm2708_mci.h +@@ -0,0 +1,101 @@ ++/* ++ * linux/drivers/mmc/host/bcm2708_mci.c - Broadcom BCM2708 MCI driver ++ * ++ * Copyright (C) 2010 Broadcom, All Rights Reserved. ++ * ++ * 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. ++ */ ++ ++struct clk; ++ ++#define BCM2708_MCI_COMMAND 0x00 ++ ++#define BCM2708_MCI_READ (1 << 6) ++#define BCM2708_MCI_WRITE (1 << 7) ++#define BCM2708_MCI_LONGRESP (1 << 9) ++#define BCM2708_MCI_NORESP (1 << 10) ++#define BCM2708_MCI_BUSY (1 << 11) ++#define BCM2708_MCI_FAIL_FLAG (1 << 14) ++#define BCM2708_MCI_ENABLE (1 << 15) ++ ++#define BCM2708_MCI_ARGUMENT 0x04 ++ ++#define BCM2708_MCI_TIMEOUT 0x08 ++#define BCM2708_MCI_CLKDIV 0x0c ++ ++ ++#define BCM2708_MCI_RESPONSE0 0x10 ++#define BCM2708_MCI_RESPONSE1 0x14 ++#define BCM2708_MCI_RESPONSE2 0x18 ++#define BCM2708_MCI_RESPONSE3 0x1c ++ ++#define BCM2708_MCI_STATUS 0x20 ++ ++#define BCM2708_MCI_VDD 0x30 ++#define BCM2708_MCI_VDD_ENABLE (1 << 0) ++ ++#define BCM2708_MCI_EDM 0x34 ++ ++#define BCM2708_MCI_HOSTCONFIG 0x38 ++ ++#define BCM2708_MCI_HOSTCONFIG_WIDE_INT_BUS 0x2 ++#define BCM2708_MCI_HOSTCONFIG_WIDEEXT_4BIT 0x4 ++#define BCM2708_MCI_HOSTCONFIG_SLOW_CARD 0x8 ++#define BCM2708_MCI_HOSTCONFIG_BLOCK_IRPT_EN (1<<8) ++#define BCM2708_MCI_HOSTCONFIG_BUSY_IRPT_EN (1<<10) ++#define BCM2708_MCI_HOSTCONFIG_WIDEEXT_CLR 0xFFFFFFFB ++ ++ ++#define BCM2708_MCI_DATAFLAG (1 << 0) ++#define BCM2708_MCI_CMDTIMEOUT (1 << 6) ++#define BCM2708_MCI_HSTS_BLOCK (1 << 9) /**< block flag in status reg */ ++#define BCM2708_MCI_HSTS_BUSY (1 << 10) /**< Busy flag in status reg */ ++ ++#define BCM2708_MCI_HBCT 0x3c ++#define BCM2708_MCI_DATA 0x40 ++#define BCM2708_MCI_HBLC 0x50 ++ ++#define NR_SG 16 ++ ++typedef struct bulk_data_struct ++{ ++ unsigned long info; ++ unsigned long src; ++ unsigned long dst; ++ unsigned long length; ++ unsigned long stride; ++ unsigned long next; ++ unsigned long pad[2]; ++} BCM2708_DMA_CB_T; ++ ++struct bcm2708_mci_host { ++ struct platform_device *dev; ++ ++ void __iomem *mmc_base; ++ void __iomem *dma_base; ++ void __iomem *gpio_base; ++ ++ BCM2708_DMA_CB_T *cb_base; ++ dma_addr_t cb_handle; ++ ++ struct mmc_host *mmc; ++ ++ struct semaphore sem; ++ ++ int is_acmd; ++ int present; ++}; ++ ++static inline char *bcm2708_mci_kmap_atomic(struct scatterlist *sg, unsigned long *flags) ++{ ++// local_irq_save(*flags); ++ return kmap_atomic(sg_page(sg), KM_BIO_SRC_IRQ) + sg->offset; ++} ++ ++static inline void bcm2708_mci_kunmap_atomic(void *buffer, unsigned long *flags) ++{ ++ kunmap_atomic(buffer, KM_BIO_SRC_IRQ); ++// local_irq_restore(*flags); ++} +--- /dev/null ++++ b/drivers/mmc/host/sdhci-bcm2708.c +@@ -0,0 +1,1461 @@ ++/* ++ * sdhci-bcm2708.c Support for SDHCI device on BCM2708 ++ * Copyright (c) 2010 Broadcom ++ * ++ * 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. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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. ++ */ ++ ++/* Supports: ++ * SDHCI platform device - Arasan SD controller in BCM2708 ++ * ++ * Inspired by sdhci-pci.c, by Pierre Ossman ++ */ ++ ++#include <linux/delay.h> ++#include <linux/highmem.h> ++#include <linux/platform_device.h> ++#include <linux/module.h> ++#include <linux/mmc/host.h> ++ ++#include <linux/io.h> ++#include <linux/dma-mapping.h> ++#include <mach/dma.h> ++#include <mach/power.h> ++ ++#include "sdhci.h" ++ ++/*****************************************************************************\ ++ * * ++ * Configuration * ++ * * ++\*****************************************************************************/ ++ ++#define DRIVER_NAME "bcm2708_sdhci" ++ ++/* for the time being insist on DMA mode - PIO seems not to work */ ++#ifndef CONFIG_MMC_SDHCI_BCM2708_DMA ++#warning Non-DMA (PIO) version of this driver currently unavailable ++#endif ++#undef CONFIG_MMC_SDHCI_BCM2708_DMA ++#define CONFIG_MMC_SDHCI_BCM2708_DMA y ++ ++#define USE_SYNC_AFTER_DMA ++#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA ++/* #define CHECK_DMA_USE */ ++#endif ++//#define LOG_REGISTERS ++ ++#define USE_SCHED_TIME ++ ++#define SDHCI_BCM_DMA_CHAN 4 /* this default is normally overriden */ ++#define SDHCI_BCM_DMA_WAITS 0 /* delays slowing DMA transfers: 0-31 */ ++/* We are worried that SD card DMA use may be blocking the AXI bus for others */ ++ ++/*! TODO: obtain these from the physical address */ ++#define DMA_SDHCI_BASE 0x7e300000 /* EMMC register block on Videocore */ ++#define DMA_SDHCI_BUFFER (DMA_SDHCI_BASE + SDHCI_BUFFER) ++ ++#define BCM2708_SDHCI_SLEEP_TIMEOUT 1000 /* msecs */ ++ ++#define POWER_OFF 0 ++#define POWER_LAZY_OFF 1 ++#define POWER_ON 2 ++ ++ ++/*****************************************************************************\ ++ * * ++ * Debug * ++ * * ++\*****************************************************************************/ ++ ++ ++ ++#define DBG(f, x...) \ ++ pr_debug(DRIVER_NAME " [%s()]: " f, __func__,## x) ++// printk(KERN_INFO DRIVER_NAME " [%s()]: " f, __func__,## x)//GRAYG ++ ++ ++/*****************************************************************************\ ++ * * ++ * High Precision Time * ++ * * ++\*****************************************************************************/ ++ ++#ifdef USE_SCHED_TIME ++ ++#include <mach/frc.h> ++ ++typedef unsigned long hptime_t; ++ ++#define FMT_HPT "lu" ++ ++static inline hptime_t hptime(void) ++{ ++ return frc_clock_ticks32(); ++} ++ ++#define HPTIME_CLK_NS 1000ul ++ ++#else ++ ++typedef unsigned long hptime_t; ++ ++#define FMT_HPT "lu" ++ ++static inline hptime_t hptime(void) ++{ ++ return jiffies; ++} ++ ++#define HPTIME_CLK_NS (1000000000ul/HZ) ++ ++#endif ++ ++static inline unsigned long int since_ns(hptime_t t) ++{ ++ return (unsigned long)((hptime() - t) * HPTIME_CLK_NS); ++} ++ ++#if 0 ++static void hptime_test(void) ++{ ++ hptime_t now; ++ hptime_t later; ++ ++ now = hptime(); ++ msleep(10); ++ later = hptime(); ++ ++ printk(KERN_INFO DRIVER_NAME": 10ms = %"FMT_HPT" clks " ++ "(from %"FMT_HPT" to %"FMT_HPT") = %luns\n", ++ later-now, now, later, ++ (unsigned long)(HPTIME_CLK_NS * (later - now))); ++ ++ now = hptime(); ++ msleep(1000); ++ later = hptime(); ++ ++ printk(KERN_INFO DRIVER_NAME": 1s = %"FMT_HPT" clks " ++ "(from %"FMT_HPT" to %"FMT_HPT") = %luns\n", ++ later-now, now, later, ++ (unsigned long)(HPTIME_CLK_NS * (later - now))); ++} ++#endif ++ ++/*****************************************************************************\ ++ * * ++ * SDHCI core callbacks * ++ * * ++\*****************************************************************************/ ++ ++ ++#ifdef CHECK_DMA_USE ++/*#define CHECK_DMA_REG_USE*/ ++#endif ++ ++#ifdef CHECK_DMA_REG_USE ++/* we don't expect anything to be using these registers during a ++ DMA (except the IRQ status) - so check */ ++static void check_dma_reg_use(struct sdhci_host *host, int reg); ++#else ++#define check_dma_reg_use(host, reg) ++#endif ++ ++ ++static inline u32 sdhci_bcm2708_raw_readl(struct sdhci_host *host, int reg) ++{ ++ return readl(host->ioaddr + reg); ++} ++ ++u32 sdhci_bcm2708_readl(struct sdhci_host *host, int reg) ++{ ++ u32 l = sdhci_bcm2708_raw_readl(host, reg); ++ ++#ifdef LOG_REGISTERS ++ printk(KERN_ERR "%s: readl from 0x%02x, value 0x%08x\n", ++ mmc_hostname(host->mmc), reg, l); ++#endif ++ check_dma_reg_use(host, reg); ++ ++ return l; ++} ++ ++u16 sdhci_bcm2708_readw(struct sdhci_host *host, int reg) ++{ ++ u32 l = sdhci_bcm2708_raw_readl(host, reg & ~3); ++ u32 w = l >> (reg << 3 & 0x18) & 0xffff; ++ ++#ifdef LOG_REGISTERS ++ printk(KERN_ERR "%s: readw from 0x%02x, value 0x%04x\n", ++ mmc_hostname(host->mmc), reg, w); ++#endif ++ check_dma_reg_use(host, reg); ++ ++ return (u16)w; ++} ++ ++u8 sdhci_bcm2708_readb(struct sdhci_host *host, int reg) ++{ ++ u32 l = sdhci_bcm2708_raw_readl(host, reg & ~3); ++ u32 b = l >> (reg << 3 & 0x18) & 0xff; ++ ++#ifdef LOG_REGISTERS ++ printk(KERN_ERR "%s: readb from 0x%02x, value 0x%02x\n", ++ mmc_hostname(host->mmc), reg, b); ++#endif ++ check_dma_reg_use(host, reg); ++ ++ return (u8)b; ++} ++ ++ ++static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg) ++{ ++ /* The Arasan has a bugette whereby it may lose the content of ++ * successive writes to registers that are within two SD-card clock ++ * cycles of each other (a clock domain crossing problem). ++ * It seems, however, that the data register does not have this problem. ++ * (Which is just as well - otherwise we'd have to nobble the DMA engine ++ * too) ++ */ ++#if 1 ++ if (reg != SDHCI_BUFFER && host->clock != 0) { ++ /* host->clock is the clock freq in Hz */ ++ static hptime_t last_write_hpt; ++ hptime_t now = hptime(); ++ unsigned int ns_2clk = 2000000000/host->clock; ++ ++ if (now == last_write_hpt || now == last_write_hpt+1) { ++ /* we can't guarantee any significant time has ++ * passed - we'll have to wait anyway ! */ ++ udelay((ns_2clk+1000-1)/1000); ++ } else ++ { ++ /* we must have waited at least this many ns: */ ++ unsigned int ns_wait = HPTIME_CLK_NS * ++ (last_write_hpt - now - 1); ++ if (ns_wait < ns_2clk) ++ udelay((ns_2clk-ns_wait+500)/1000); ++ } ++ last_write_hpt = now; ++ } ++ writel(val, host->ioaddr + reg); ++#else ++ void __iomem * regaddr = host->ioaddr + reg; ++ ++ writel(val, regaddr); ++ ++ if (reg != SDHCI_BUFFER && reg != SDHCI_INT_STATUS && host->clock != 0) ++ { ++ int timeout = 100000; ++ while (val != readl(regaddr) && --timeout > 0) ++ continue; ++ ++ if (timeout <= 0) ++ printk(KERN_ERR "%s: writing 0x%X to reg 0x%X " ++ "always gives 0x%X\n", ++ mmc_hostname(host->mmc), ++ val, reg, readl(regaddr)); ++ BUG_ON(timeout <= 0); ++ } ++#endif ++} ++ ++ ++void sdhci_bcm2708_writel(struct sdhci_host *host, u32 val, int reg) ++{ ++#ifdef LOG_REGISTERS ++ printk(KERN_ERR "%s: writel to 0x%02x, value 0x%08x\n", ++ mmc_hostname(host->mmc), reg, val); ++#endif ++ check_dma_reg_use(host, reg); ++ ++ sdhci_bcm2708_raw_writel(host, val, reg); ++} ++ ++void sdhci_bcm2708_writew(struct sdhci_host *host, u16 val, int reg) ++{ ++ static u32 shadow = 0; ++ ++ u32 p = reg == SDHCI_COMMAND ? shadow : ++ sdhci_bcm2708_raw_readl(host, reg & ~3); ++ u32 s = reg << 3 & 0x18; ++ u32 l = val << s; ++ u32 m = 0xffff << s; ++ ++#ifdef LOG_REGISTERS ++ printk(KERN_ERR "%s: writew to 0x%02x, value 0x%04x\n", ++ mmc_hostname(host->mmc), reg, val); ++#endif ++ ++ if (reg == SDHCI_TRANSFER_MODE) ++ shadow = (p & ~m) | l; ++ else { ++ check_dma_reg_use(host, reg); ++ sdhci_bcm2708_raw_writel(host, (p & ~m) | l, reg & ~3); ++ } ++} ++ ++void sdhci_bcm2708_writeb(struct sdhci_host *host, u8 val, int reg) ++{ ++ u32 p = sdhci_bcm2708_raw_readl(host, reg & ~3); ++ u32 s = reg << 3 & 0x18; ++ u32 l = val << s; ++ u32 m = 0xff << s; ++ ++#ifdef LOG_REGISTERS ++ printk(KERN_ERR "%s: writeb to 0x%02x, value 0x%02x\n", ++ mmc_hostname(host->mmc), reg, val); ++#endif ++ ++ check_dma_reg_use(host, reg); ++ sdhci_bcm2708_raw_writel(host, (p & ~m) | l, reg & ~3); ++} ++ ++static unsigned int sdhci_bcm2708_get_max_clock(struct sdhci_host *host) ++{ ++ return 100000000; // this value is in Hz (100MHz/4) ++} ++ ++static unsigned int sdhci_bcm2708_get_timeout_clock(struct sdhci_host *host) ++{ ++ return 100000; // this value is in kHz (100MHz/4) ++} ++ ++/*****************************************************************************\ ++ * * ++ * DMA Operation * ++ * * ++\*****************************************************************************/ ++ ++struct sdhci_bcm2708_priv { ++ int dma_chan; ++ int dma_irq; ++ void __iomem *dma_chan_base; ++ struct bcm2708_dma_cb *cb_base; /* DMA control blocks */ ++ dma_addr_t cb_handle; ++ /* tracking scatter gather progress */ ++ unsigned sg_ix; /* scatter gather list index */ ++ unsigned sg_done; /* bytes in current sg_ix done */ ++ /* power management */ ++ BCM_POWER_HANDLE_T power_handle; ++ unsigned char power_state; /* enable/disable power state */ ++ unsigned char power_mode; /* last set power mode */ ++#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA ++ unsigned char dma_wanted; /* DMA transfer requested */ ++ unsigned char dma_waits; /* wait states in DMAs */ ++#ifdef CHECK_DMA_USE ++ unsigned char dmas_pending; /* no of unfinished DMAs */ ++ hptime_t when_started; ++ hptime_t when_reset; ++ hptime_t when_stopped; ++#endif ++#endif ++ /* signalling the end of a transfer */ ++ void (*complete)(struct sdhci_host *); ++}; ++ ++#define SDHCI_HOST_PRIV(host) \ ++ (struct sdhci_bcm2708_priv *)((struct sdhci_host *)(host)+1) ++ ++ ++ ++#ifdef CHECK_DMA_REG_USE ++static void check_dma_reg_use(struct sdhci_host *host, int reg) ++{ ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ if (host_priv->dma_wanted && reg != SDHCI_INT_STATUS) { ++ printk(KERN_INFO"%s: accessing register 0x%x during DMA\n", ++ mmc_hostname(host->mmc), reg); ++ } ++} ++#endif ++ ++ ++ ++#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA ++ ++static void sdhci_clear_set_irqgen(struct sdhci_host *host, u32 clear, u32 set) ++{ ++ u32 ier; ++ ++ ier = sdhci_bcm2708_raw_readl(host, SDHCI_SIGNAL_ENABLE); ++ ier &= ~clear; ++ ier |= set; ++ /* change which requests generate IRQs - makes no difference to ++ the content of SDHCI_INT_STATUS, or the need to acknowledge IRQs */ ++ sdhci_bcm2708_raw_writel(host, ier, SDHCI_SIGNAL_ENABLE); ++} ++ ++static void sdhci_signal_irqs(struct sdhci_host *host, u32 irqs) ++{ ++ sdhci_clear_set_irqgen(host, 0, irqs); ++} ++ ++static void sdhci_unsignal_irqs(struct sdhci_host *host, u32 irqs) ++{ ++ sdhci_clear_set_irqgen(host, irqs, 0); ++} ++ ++ ++ ++static void schci_bcm2708_cb_read(struct sdhci_bcm2708_priv *host, ++ int ix, ++ dma_addr_t dma_addr, unsigned len, ++ int /*bool*/ is_last) ++{ ++ struct bcm2708_dma_cb *cb = &host->cb_base[ix]; ++ unsigned char dmawaits = host->dma_waits; ++ ++ cb->info = BCM2708_DMA_PER_MAP(BCM2708_DMA_DREQ_EMMC) | ++ BCM2708_DMA_WAITS(dmawaits) | ++ BCM2708_DMA_S_DREQ | ++ BCM2708_DMA_D_WIDTH | ++ BCM2708_DMA_D_INC; ++ cb->src = DMA_SDHCI_BUFFER; /* DATA register DMA address */ ++ cb->dst = dma_addr; ++ cb->length = len; ++ cb->stride = 0; ++ ++ if (is_last) { ++ cb->info |= BCM2708_DMA_INT_EN; ++ cb->next = 0; ++ } else ++ cb->next = host->cb_handle + ++ (ix+1)*sizeof(struct bcm2708_dma_cb); ++ ++ cb->pad[0] = 0; ++ cb->pad[1] = 0; ++} ++ ++static void schci_bcm2708_cb_write(struct sdhci_bcm2708_priv *host, ++ int ix, ++ dma_addr_t dma_addr, unsigned len, ++ int /*bool*/ is_last) ++{ ++ struct bcm2708_dma_cb *cb = &host->cb_base[ix]; ++ unsigned char dmawaits = host->dma_waits; ++ ++ /* We can make arbitrarily large writes as long as we specify DREQ to ++ pace the delivery of bytes to the Arasan hardware */ ++ cb->info = BCM2708_DMA_PER_MAP(BCM2708_DMA_DREQ_EMMC) | ++ BCM2708_DMA_WAITS(dmawaits) | ++ BCM2708_DMA_D_DREQ | ++ BCM2708_DMA_S_WIDTH | ++ BCM2708_DMA_S_INC; ++ cb->src = dma_addr; ++ cb->dst = DMA_SDHCI_BUFFER; /* DATA register DMA address */ ++ cb->length = len; ++ cb->stride = 0; ++ ++ if (is_last) { ++ cb->info |= BCM2708_DMA_INT_EN; ++ cb->next = 0; ++ } else ++ cb->next = host->cb_handle + ++ (ix+1)*sizeof(struct bcm2708_dma_cb); ++ ++ cb->pad[0] = 0; ++ cb->pad[1] = 0; ++} ++ ++ ++static void schci_bcm2708_dma_go(struct sdhci_host *host) ++{ ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ void __iomem *dma_chan_base = host_priv->dma_chan_base; ++ ++ BUG_ON(host_priv->dma_wanted); ++#ifdef CHECK_DMA_USE ++ if (host_priv->dma_wanted) ++ printk(KERN_ERR "%s: DMA already in progress - " ++ "now %"FMT_HPT", last started %lu " ++ "reset %lu stopped %lu\n", ++ mmc_hostname(host->mmc), ++ hptime(), since_ns(host_priv->when_started), ++ since_ns(host_priv->when_reset), ++ since_ns(host_priv->when_stopped)); ++ else if (host_priv->dmas_pending > 0) ++ printk(KERN_INFO "%s: note - new DMA when %d reset DMAs " ++ "already in progress - " ++ "now %"FMT_HPT", started %lu reset %lu stopped %lu\n", ++ mmc_hostname(host->mmc), ++ host_priv->dmas_pending, ++ hptime(), since_ns(host_priv->when_started), ++ since_ns(host_priv->when_reset), ++ since_ns(host_priv->when_stopped)); ++ host_priv->dmas_pending += 1; ++ host_priv->when_started = hptime(); ++#endif ++ host_priv->dma_wanted = 1; ++ DBG("PDMA go - base %p handle %08X\n", dma_chan_base, ++ host_priv->cb_handle); ++ bcm_dma_start(dma_chan_base, host_priv->cb_handle); ++} ++ ++ ++static void ++sdhci_platdma_read(struct sdhci_host *host, dma_addr_t dma_addr, size_t len) ++{ ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ ++ DBG("PDMA to read %d bytes\n", len); ++ host_priv->sg_done += len; ++ schci_bcm2708_cb_read(host_priv, 0, dma_addr, len, 1/*TRUE*/); ++ schci_bcm2708_dma_go(host); ++} ++ ++ ++static void ++sdhci_platdma_write(struct sdhci_host *host, dma_addr_t dma_addr, size_t len) ++{ ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ ++ DBG("PDMA to write %d bytes\n", len); ++ //BUG_ON(0 != (len & 0x1ff)); ++ ++ host_priv->sg_done += len; ++ schci_bcm2708_cb_write(host_priv, 0, dma_addr, len, 1/*TRUE*/); ++ schci_bcm2708_dma_go(host); ++} ++ ++/*! space is avaiable to receive into or data is available to write ++ Platform DMA exported function ++*/ ++void ++sdhci_bcm2708_platdma_avail(struct sdhci_host *host, unsigned int *ref_intmask, ++ void(*completion_callback)(struct sdhci_host *host)) ++{ ++ struct mmc_data *data = host->data; ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ int sg_ix; ++ size_t bytes; ++ dma_addr_t addr; ++ ++ BUG_ON(NULL == data); ++ BUG_ON(0 == data->blksz); ++ ++ host_priv->complete = completion_callback; ++ ++ sg_ix = host_priv->sg_ix; ++ BUG_ON(sg_ix >= data->sg_len); ++ ++ /* we can DMA blocks larger than blksz - it may hang the DMA ++ channel but we are its only user */ ++ bytes = sg_dma_len(&data->sg[sg_ix]) - host_priv->sg_done; ++ addr = sg_dma_address(&data->sg[sg_ix]) + host_priv->sg_done; ++ ++ if (bytes > 0) { ++ /* We're going to poll for read/write available state until ++ we finish this DMA ++ */ ++ ++ if (data->flags & MMC_DATA_READ) { ++ if (*ref_intmask & SDHCI_INT_DATA_AVAIL) { ++ sdhci_unsignal_irqs(host, SDHCI_INT_DATA_AVAIL | ++ SDHCI_INT_SPACE_AVAIL); ++ sdhci_platdma_read(host, addr, bytes); ++ } ++ } else { ++ if (*ref_intmask & SDHCI_INT_SPACE_AVAIL) { ++ sdhci_unsignal_irqs(host, SDHCI_INT_DATA_AVAIL | ++ SDHCI_INT_SPACE_AVAIL); ++ sdhci_platdma_write(host, addr, bytes); ++ } ++ } ++ } ++ /* else: ++ we have run out of bytes that need transferring (e.g. we may be in ++ the middle of the last DMA transfer), or ++ it is also possible that we've been called when another IRQ is ++ signalled, even though we've turned off signalling of our own IRQ */ ++ ++ *ref_intmask &= ~SDHCI_INT_DATA_END; ++ /* don't let the main sdhci driver act on this .. we'll deal with it ++ when we respond to the DMA - if one is currently in progress */ ++} ++ ++/* is it possible to DMA the given mmc_data structure? ++ Platform DMA exported function ++*/ ++int /*bool*/ ++sdhci_bcm2708_platdma_dmaable(struct sdhci_host *host, struct mmc_data *data) ++{ ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ int ok = bcm_sg_suitable_for_dma(data->sg, data->sg_len); ++ ++ if (!ok) ++ DBG("Reverting to PIO - bad cache alignment\n"); ++ ++ else { ++ host_priv->sg_ix = 0; /* first SG index */ ++ host_priv->sg_done = 0; /* no bytes done */ ++ } ++ ++ return ok; ++} ++ ++#include <mach/arm_control.h> //GRAYG ++/*! the current SD transacton has been abandonned ++ We need to tidy up if we were in the middle of a DMA ++ Platform DMA exported function ++*/ ++void ++sdhci_bcm2708_platdma_reset(struct sdhci_host *host, struct mmc_data *data) ++{ ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ unsigned long flags; ++ ++ BUG_ON(NULL == host); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ if (host_priv->dma_wanted) { ++ if (NULL == data) { ++ printk(KERN_ERR "%s: ongoing DMA reset - no data!\n", ++ mmc_hostname(host->mmc)); ++ BUG_ON(NULL == data); ++ } else { ++ struct scatterlist *sg; ++ int sg_len; ++ int sg_todo; ++ int rc; ++ unsigned long cs; ++ ++ sg = data->sg; ++ sg_len = data->sg_len; ++ sg_todo = sg_dma_len(&sg[host_priv->sg_ix]); ++ ++ cs = readl(host_priv->dma_chan_base + BCM2708_DMA_CS); ++ ++ if (!(BCM2708_DMA_ACTIVE & cs)) ++ printk(KERN_INFO "%s: missed completion of " ++ "cmd %d DMA (%d/%d [%d]/[%d]) - " ++ "ignoring it\n", ++ mmc_hostname(host->mmc), ++ host->last_cmdop, ++ host_priv->sg_done, sg_todo, ++ host_priv->sg_ix+1, sg_len); ++ else ++ printk(KERN_INFO "%s: resetting ongoing cmd %d" ++ "DMA before %d/%d [%d]/[%d] complete\n", ++ mmc_hostname(host->mmc), ++ host->last_cmdop, ++ host_priv->sg_done, sg_todo, ++ host_priv->sg_ix+1, sg_len); ++#ifdef CHECK_DMA_USE ++ printk(KERN_INFO "%s: now %"FMT_HPT" started %lu " ++ "last reset %lu last stopped %lu\n", ++ mmc_hostname(host->mmc), ++ hptime(), since_ns(host_priv->when_started), ++ since_ns(host_priv->when_reset), ++ since_ns(host_priv->when_stopped)); ++ { unsigned long info, debug; ++ void __iomem *base; ++ unsigned long pend0, pend1, pend2; ++ ++ base = host_priv->dma_chan_base; ++ cs = readl(base + BCM2708_DMA_CS); ++ info = readl(base + BCM2708_DMA_INFO); ++ debug = readl(base + BCM2708_DMA_DEBUG); ++ printk(KERN_INFO "%s: DMA%d CS=%08lX TI=%08lX " ++ "DEBUG=%08lX\n", ++ mmc_hostname(host->mmc), ++ host_priv->dma_chan, ++ cs, info, debug); ++ pend0 = readl(__io_address(ARM_IRQ_PEND0)); ++ pend1 = readl(__io_address(ARM_IRQ_PEND1)); ++ pend2 = readl(__io_address(ARM_IRQ_PEND2)); ++ ++ printk(KERN_INFO "%s: PEND0=%08lX " ++ "PEND1=%08lX PEND2=%08lX\n", ++ mmc_hostname(host->mmc), ++ pend0, pend1, pend2); ++ ++ //gintsts = readl(__io_address(GINTSTS)); ++ //gintmsk = readl(__io_address(GINTMSK)); ++ //printk(KERN_INFO "%s: USB GINTSTS=%08lX" ++ // "GINTMSK=%08lX\n", ++ // mmc_hostname(host->mmc), gintsts, gintmsk); ++ } ++#endif ++ rc = bcm_dma_abort(host_priv->dma_chan_base); ++ BUG_ON(rc != 0); ++ } ++ host_priv->dma_wanted = 0; ++#ifdef CHECK_DMA_USE ++ host_priv->when_reset = hptime(); ++#endif ++ } ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++ ++static void sdhci_bcm2708_dma_complete_irq(struct sdhci_host *host, ++ u32 dma_cs) ++{ ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ struct mmc_data *data; ++ struct scatterlist *sg; ++ int sg_len; ++ int sg_ix; ++ int sg_todo; ++ unsigned long flags; ++ ++ BUG_ON(NULL == host); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ data = host->data; ++ ++#ifdef CHECK_DMA_USE ++ if (host_priv->dmas_pending <= 0) ++ DBG("on completion no DMA in progress - " ++ "now %"FMT_HPT" started %lu reset %lu stopped %lu\n", ++ hptime(), since_ns(host_priv->when_started), ++ since_ns(host_priv->when_reset), ++ since_ns(host_priv->when_stopped)); ++ else if (host_priv->dmas_pending > 1) ++ DBG("still %d DMA in progress after completion - " ++ "now %"FMT_HPT" started %lu reset %lu stopped %lu\n", ++ host_priv->dmas_pending - 1, ++ hptime(), since_ns(host_priv->when_started), ++ since_ns(host_priv->when_reset), ++ since_ns(host_priv->when_stopped)); ++ BUG_ON(host_priv->dmas_pending <= 0); ++ host_priv->dmas_pending -= 1; ++ host_priv->when_stopped = hptime(); ++#endif ++ host_priv->dma_wanted = 0; ++ ++ if (NULL == data) { ++ DBG("PDMA unused completion - status 0x%X\n", dma_cs); ++ spin_unlock_irqrestore(&host->lock, flags); ++ return; ++ } ++ sg = data->sg; ++ sg_len = data->sg_len; ++ sg_todo = sg_dma_len(&sg[host_priv->sg_ix]); ++ ++ DBG("PDMA complete %d/%d [%d]/[%d]..\n", ++ host_priv->sg_done, sg_todo, ++ host_priv->sg_ix+1, sg_len); ++ ++ BUG_ON(host_priv->sg_done > sg_todo); ++ ++ if (host_priv->sg_done >= sg_todo) { ++ host_priv->sg_ix++; ++ host_priv->sg_done = 0; ++ } ++ ++ sg_ix = host_priv->sg_ix; ++ if (sg_ix < sg_len) { ++ u32 irq_mask; ++ /* Set off next DMA if we've got the capacity */ ++ ++ if (data->flags & MMC_DATA_READ) ++ irq_mask = SDHCI_INT_DATA_AVAIL; ++ else ++ irq_mask = SDHCI_INT_SPACE_AVAIL; ++ ++ /* We have to use the interrupt status register on the BCM2708 ++ rather than the SDHCI_PRESENT_STATE register because latency ++ in the glue logic means that the information retrieved from ++ the latter is not always up-to-date w.r.t the DMA engine - ++ it may not indicate that a read or a write is ready yet */ ++ if (sdhci_bcm2708_raw_readl(host, SDHCI_INT_STATUS) & ++ irq_mask) { ++ size_t bytes = sg_dma_len(&sg[sg_ix]) - ++ host_priv->sg_done; ++ dma_addr_t addr = sg_dma_address(&data->sg[sg_ix]) + ++ host_priv->sg_done; ++ ++ /* acknowledge interrupt */ ++ sdhci_bcm2708_raw_writel(host, irq_mask, ++ SDHCI_INT_STATUS); ++ ++ BUG_ON(0 == bytes); ++ ++ if (data->flags & MMC_DATA_READ) ++ sdhci_platdma_read(host, addr, bytes); ++ else ++ sdhci_platdma_write(host, addr, bytes); ++ } else { ++ DBG("PDMA - wait avail\n"); ++ /* may generate an IRQ if already present */ ++ sdhci_signal_irqs(host, SDHCI_INT_DATA_AVAIL | ++ SDHCI_INT_SPACE_AVAIL); ++ } ++ } else { ++#ifdef USE_SYNC_AFTER_DMA ++ /* On the Arasan controller the stop command (which will be ++ scheduled after this completes) does not seem to work ++ properly if we allow it to be issued when we are ++ transferring data to/from the SD card. ++ We get CRC and DEND errors unless we wait for ++ the SD controller to finish reading/writing to the card. */ ++ u32 state_mask; ++ int timeout=1000000; ++ hptime_t now = hptime(); ++ ++ DBG("PDMA over - sync card\n"); ++ if (data->flags & MMC_DATA_READ) ++ state_mask = SDHCI_DOING_READ; ++ else ++ state_mask = SDHCI_DOING_WRITE; ++ ++ while (0 != (sdhci_bcm2708_raw_readl(host, ++ SDHCI_PRESENT_STATE) & ++ state_mask) && --timeout > 0) ++ continue; ++ ++ if (1000000-timeout > 4000) /*ave. is about 3250*/ ++ printk(KERN_INFO "%s: note - long %s sync %luns - " ++ "%d its.\n", ++ mmc_hostname(host->mmc), ++ data->flags & MMC_DATA_READ? "read": "write", ++ since_ns(now), 1000000-timeout); ++ if (timeout <= 0) ++ printk(KERN_ERR"%s: final %s to SD card still " ++ "running\n", ++ mmc_hostname(host->mmc), ++ data->flags & MMC_DATA_READ? "read": "write"); ++#endif ++ if (host_priv->complete) { ++ (*host_priv->complete)(host); ++ DBG("PDMA %s complete\n", ++ data->flags & MMC_DATA_READ?"read":"write"); ++ sdhci_signal_irqs(host, SDHCI_INT_DATA_AVAIL | ++ SDHCI_INT_SPACE_AVAIL); ++ } ++ } ++ spin_unlock_irqrestore(&host->lock, flags); ++} ++ ++static irqreturn_t sdhci_bcm2708_dma_irq(int irq, void *dev_id) ++{ ++ irqreturn_t result = IRQ_NONE; ++ struct sdhci_host *host = dev_id; ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ u32 dma_cs; /* control and status register */ ++ unsigned long flags; ++ ++ BUG_ON(NULL == dev_id); ++ BUG_ON(NULL == host_priv->dma_chan_base); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ dma_cs = readl(host_priv->dma_chan_base + BCM2708_DMA_CS); ++ ++ if (dma_cs & BCM2708_DMA_ERR) { ++ unsigned long debug; ++ debug = readl(host_priv->dma_chan_base + ++ BCM2708_DMA_DEBUG); ++ printk(KERN_ERR "%s: DMA error - CS %lX DEBUG %lX\n", ++ mmc_hostname(host->mmc), (unsigned long)dma_cs, ++ (unsigned long)debug); ++ /* reset error */ ++ writel(debug, host_priv->dma_chan_base + ++ BCM2708_DMA_DEBUG); ++ } ++ if (dma_cs & BCM2708_DMA_INT) { ++ /* acknowledge interrupt */ ++ writel(BCM2708_DMA_INT, ++ host_priv->dma_chan_base + BCM2708_DMA_CS); ++ ++ dsb(); /* ARM data synchronization (push) operation */ ++ ++ if (!host_priv->dma_wanted) { ++ /* ignore this interrupt - it was reset */ ++ printk(KERN_INFO "%s: DMA IRQ %X ignored - " ++ "results were reset\n", ++ mmc_hostname(host->mmc), dma_cs); ++#ifdef CHECK_DMA_USE ++ printk(KERN_INFO "%s: now %"FMT_HPT ++ " started %lu reset %lu stopped %lu\n", ++ mmc_hostname(host->mmc), hptime(), ++ since_ns(host_priv->when_started), ++ since_ns(host_priv->when_reset), ++ since_ns(host_priv->when_stopped)); ++ host_priv->dmas_pending--; ++#endif ++ } else ++ sdhci_bcm2708_dma_complete_irq(host, dma_cs); ++ ++ result = IRQ_HANDLED; ++ } ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ return result; ++} ++#endif /* CONFIG_MMC_SDHCI_BCM2708_DMA */ ++ ++ ++/***************************************************************************** \ ++ * * ++ * Device Attributes * ++ * * ++\*****************************************************************************/ ++ ++ ++/** ++ * Show the DMA-using status ++ */ ++static ssize_t attr_dma_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct sdhci_host *host = (struct sdhci_host *)dev_get_drvdata(_dev); ++ ++ if (host) { ++ int use_dma = (host->flags & SDHCI_USE_PLATDMA? 1:0); ++ return sprintf(buf, "%d\n", use_dma); ++ } else ++ return -EINVAL; ++} ++ ++/** ++ * Set the DMA-using status ++ */ ++static ssize_t attr_dma_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct sdhci_host *host = (struct sdhci_host *)dev_get_drvdata(_dev); ++ ++ if (host) { ++#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA ++ int on = simple_strtol(buf, NULL, 0); ++ if (on) { ++ host->flags |= SDHCI_USE_PLATDMA; ++ printk(KERN_INFO "%s: DMA enabled\n", ++ mmc_hostname(host->mmc)); ++ } else { ++ host->flags &= ~(SDHCI_USE_PLATDMA | SDHCI_REQ_USE_DMA); ++ printk(KERN_INFO "%s: DMA disabled\n", ++ mmc_hostname(host->mmc)); ++ } ++#endif ++ return count; ++ } else ++ return -EINVAL; ++} ++ ++static DEVICE_ATTR(use_dma, S_IRUGO | S_IWUGO, attr_dma_show, attr_dma_store); ++ ++ ++/** ++ * Show the DMA wait states used ++ */ ++static ssize_t attr_dmawait_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct sdhci_host *host = (struct sdhci_host *)dev_get_drvdata(_dev); ++ ++ if (host) { ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ int dmawait = host_priv->dma_waits; ++ return sprintf(buf, "%d\n", dmawait); ++ } else ++ return -EINVAL; ++} ++ ++/** ++ * Set the DMA wait state used ++ */ ++static ssize_t attr_dmawait_store(struct device *_dev, ++ struct device_attribute *attr, ++ const char *buf, size_t count) ++{ ++ struct sdhci_host *host = (struct sdhci_host *)dev_get_drvdata(_dev); ++ ++ if (host) { ++#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ int dma_waits = simple_strtol(buf, NULL, 0); ++ if (dma_waits >= 0 && dma_waits < 32) ++ host_priv->dma_waits = dma_waits; ++ else ++ printk(KERN_ERR "%s: illegal dma_waits value - %d", ++ mmc_hostname(host->mmc), dma_waits); ++#endif ++ return count; ++ } else ++ return -EINVAL; ++} ++ ++static DEVICE_ATTR(dma_wait, S_IRUGO | S_IWUGO, ++ attr_dmawait_show, attr_dmawait_store); ++ ++ ++/** ++ * Show the DMA-using status ++ */ ++static ssize_t attr_status_show(struct device *_dev, ++ struct device_attribute *attr, char *buf) ++{ ++ struct sdhci_host *host = (struct sdhci_host *)dev_get_drvdata(_dev); ++ ++ if (host) { ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ int power_state = host_priv->power_state; ++ return sprintf(buf, ++ "present: yes\n" ++ "power: %s\n" ++ "clock: %u Hz\n" ++#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA ++ "dma: %s (%d waits)\n", ++#else ++ "dma: unconfigured\n", ++#endif ++ power_state == POWER_ON? "on": ++ power_state == POWER_OFF? "off": ++ power_state == POWER_LAZY_OFF? "lazy-off": ++ "<unknown>", ++ host->clock ++#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA ++ , (host->flags & SDHCI_USE_PLATDMA)? "on": "off" ++ , host_priv->dma_waits ++#endif ++ ); ++ } else ++ return -EINVAL; ++} ++ ++static DEVICE_ATTR(status, S_IRUGO, attr_status_show, NULL); ++ ++/***************************************************************************** \ ++ * * ++ * Power Management * ++ * * ++\*****************************************************************************/ ++ ++ ++#ifdef CONFIG_PM ++static int sdhci_bcm2708_suspend(struct platform_device *dev, pm_message_t state) ++{ ++ struct sdhci_host *host = (struct sdhci_host *) ++ platform_get_drvdata(dev); ++ int ret = 0; ++ ++ if (host->mmc) { ++ ret = mmc_suspend_host(host->mmc); ++ } ++ ++ return ret; ++} ++ ++static int sdhci_bcm2708_resume(struct platform_device *dev) ++{ ++ struct sdhci_host *host = (struct sdhci_host *) ++ platform_get_drvdata(dev); ++ int ret = 0; ++ ++ if (host->mmc) { ++ ret = mmc_resume_host(host->mmc); ++ } ++ ++ return ret; ++} ++#endif ++ ++ ++/* Click forwards one step towards fully on */ ++static int sdhci_bcm2708_enable(struct sdhci_host *host) ++{ ++ int rc; ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ ++ if (host_priv->power_state == POWER_OFF) { ++ /* warning: may schedule - don't call in irq mode */ ++ rc = bcm_power_request(host_priv->power_handle, ++ BCM_POWER_SDCARD); ++ ++ if (rc == 0) { ++ mmc_power_restore_host(host->mmc); ++ host_priv->power_state = POWER_ON; ++ } else if (rc != -EINTR) ++ printk(KERN_ERR "%s: mmc power up request failed - " ++ "rc %d\n", ++ mmc_hostname(host->mmc), rc); ++ } else { ++ host_priv->power_state = POWER_ON; ++ rc = 0; ++ } ++ ++ return rc; ++} ++ ++/* Click backwards one step towards fully off */ ++static int sdhci_bcm2708_disable(struct sdhci_host *host, int lazy) ++{ ++ int rc; ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ ++ if ((host_priv->power_state == POWER_ON) && lazy) { ++ host_priv->power_state = POWER_LAZY_OFF; ++ return BCM2708_SDHCI_SLEEP_TIMEOUT; ++ } ++ ++ /* warning: may schedule - don't call in irq mode */ ++ rc = bcm_power_request(host_priv->power_handle, BCM_POWER_NONE); ++ ++ if (rc == 0) ++ host_priv->power_state = POWER_OFF; ++ else if (rc != -EINTR) ++ printk(KERN_ERR "%s: mmc power down request failed - rc %d\n", ++ mmc_hostname(host->mmc), rc); ++ ++ return rc; ++} ++ ++static int sdhci_bcm2708_set_plat_power(struct sdhci_host *host, ++ int power_mode) ++{ ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ int rc; ++ ++ do { ++ rc = mmc_host_enable(host->mmc); ++ } while (-EINTR == rc); ++ ++ if (rc == 0) do { ++ if (rc == 0 && power_mode != host_priv->power_mode) ++ { ++ switch (power_mode) ++ { ++ case MMC_POWER_OFF: ++ rc = bcm_power_request(host_priv->power_handle, ++ BCM_POWER_NONE); ++ break; ++ ++ case MMC_POWER_UP: ++ rc = bcm_power_request(host_priv->power_handle, ++ BCM_POWER_SDCARD); ++ /* ++ * We need an extra 10ms delay of 10ms before we ++ * can apply clock after applying power ++ */ ++ if (rc == 0) ++ mdelay(10); ++ break; ++ ++ case MMC_POWER_ON: ++ mdelay(10); ++ /* do_send_init_stream = 1; */ ++ break; ++ } ++ ++ if (rc == 0) ++ host_priv->power_mode = power_mode; ++ } ++ } while (-EINTR == rc); ++ ++ if (rc == 0) do { ++ if (rc == 0) { ++ if (power_mode == MMC_POWER_OFF) ++ rc = mmc_host_disable(host->mmc); ++ else ++ rc = mmc_host_lazy_disable(host->mmc); ++ } ++ ++ } while (-EINTR == rc); ++ ++ return rc; ++} ++ ++/*****************************************************************************\ ++ * * ++ * Device quirk functions. Implemented as local ops because the flags * ++ * field is out of space with newer kernels. This implementation can be * ++ * back ported to older kernels as well. * ++\****************************************************************************/ ++static unsigned int sdhci_bcm2708_quirk_extra_ints(struct sdhci_host *host) ++{ ++ return 1; ++} ++ ++static unsigned int sdhci_bcm2708_quirk_spurious_crc(struct sdhci_host *host) ++{ ++ return 1; ++} ++ ++static unsigned int sdhci_bcm2708_quirk_voltage_broken(struct sdhci_host *host) ++{ ++ return 1; ++} ++ ++static unsigned int sdhci_bcm2708_uhs_broken(struct sdhci_host *host) ++{ ++ return 1; ++} ++ ++/***************************************************************************** \ ++ * * ++ * Device ops * ++ * * ++\*****************************************************************************/ ++ ++static struct sdhci_ops sdhci_bcm2708_ops = { ++#ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS ++ .read_l = sdhci_bcm2708_readl, ++ .read_w = sdhci_bcm2708_readw, ++ .read_b = sdhci_bcm2708_readb, ++ .write_l = sdhci_bcm2708_writel, ++ .write_w = sdhci_bcm2708_writew, ++ .write_b = sdhci_bcm2708_writeb, ++#else ++#error The BCM2708 SDHCI driver needs CONFIG_MMC_SDHCI_IO_ACCESSORS to be set ++#endif ++ //.enable_dma = NULL, ++ //.set_clock = NULL, ++ .get_max_clock = sdhci_bcm2708_get_max_clock, ++ //.get_min_clock = NULL, ++ .get_timeout_clock = sdhci_bcm2708_get_timeout_clock, ++ ++ .enable = sdhci_bcm2708_enable, ++ .disable = sdhci_bcm2708_disable, ++ .set_plat_power = sdhci_bcm2708_set_plat_power, ++ ++#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA ++ // Platform DMA operations ++ .pdma_able = sdhci_bcm2708_platdma_dmaable, ++ .pdma_avail = sdhci_bcm2708_platdma_avail, ++ .pdma_reset = sdhci_bcm2708_platdma_reset, ++#endif ++ .extra_ints = sdhci_bcm2708_quirk_extra_ints, ++ .spurious_crc_acmd51 = sdhci_bcm2708_quirk_spurious_crc, ++ .voltage_broken = sdhci_bcm2708_quirk_voltage_broken, ++ .uhs_broken = sdhci_bcm2708_uhs_broken, ++}; ++ ++/*****************************************************************************\ ++ * * ++ * Device probing/removal * ++ * * ++\*****************************************************************************/ ++ ++static int __devinit sdhci_bcm2708_probe(struct platform_device *pdev) ++{ ++ struct sdhci_host *host; ++ struct resource *iomem; ++ struct sdhci_bcm2708_priv *host_priv; ++ int ret; ++ ++ BUG_ON(pdev == NULL); ++ ++ iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ if (!iomem) { ++ ret = -ENOMEM; ++ goto err; ++ } ++ ++ if (resource_size(iomem) != 0x100) ++ dev_err(&pdev->dev, "Invalid iomem size. You may " ++ "experience problems.\n"); ++ ++ if (pdev->dev.parent) ++ host = sdhci_alloc_host(pdev->dev.parent, ++ sizeof(struct sdhci_bcm2708_priv)); ++ else ++ host = sdhci_alloc_host(&pdev->dev, ++ sizeof(struct sdhci_bcm2708_priv)); ++ ++ if (IS_ERR(host)) { ++ ret = PTR_ERR(host); ++ goto err; ++ } ++ ++ host->hw_name = "BCM2708_Arasan"; ++ host->ops = &sdhci_bcm2708_ops; ++ host->irq = platform_get_irq(pdev, 0); ++ ++ host->quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | ++ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; ++#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA ++ host->flags = SDHCI_USE_PLATDMA; ++#endif ++ ++ if (!request_mem_region(iomem->start, resource_size(iomem), ++ mmc_hostname(host->mmc))) { ++ dev_err(&pdev->dev, "cannot request region\n"); ++ ret = -EBUSY; ++ goto err_request; ++ } ++ ++ host->ioaddr = ioremap(iomem->start, resource_size(iomem)); ++ if (!host->ioaddr) { ++ dev_err(&pdev->dev, "failed to remap registers\n"); ++ ret = -ENOMEM; ++ goto err_remap; ++ } ++ ++ host_priv = SDHCI_HOST_PRIV(host); ++ ++ host_priv->power_state = POWER_ON; ++ ret = bcm_power_open(&host_priv->power_handle); ++ if (ret != 0) ++ goto err_power; ++ ++#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA ++ host_priv->dma_wanted = 0; ++#ifdef CHECK_DMA_USE ++ host_priv->dmas_pending = 0; ++ host_priv->when_started = 0; ++ host_priv->when_reset = 0; ++ host_priv->when_stopped = 0; ++#endif ++ host_priv->sg_ix = 0; ++ host_priv->sg_done = 0; ++ host_priv->complete = NULL; ++ host_priv->dma_waits = SDHCI_BCM_DMA_WAITS; ++ ++ host_priv->cb_base = dma_alloc_writecombine(&pdev->dev, SZ_4K, ++ &host_priv->cb_handle, ++ GFP_KERNEL); ++ if (!host_priv->cb_base) { ++ dev_err(&pdev->dev, "cannot allocate DMA CBs\n"); ++ ret = -ENOMEM; ++ goto err_alloc_cb; ++ } ++ ++ ret = bcm_dma_chan_alloc(BCM_DMA_FEATURE_FAST, ++ &host_priv->dma_chan_base, ++ &host_priv->dma_irq); ++ if (ret < 0) { ++ dev_err(&pdev->dev, "couldn't allocate a DMA channel\n"); ++ goto err_add_dma; ++ } ++ host_priv->dma_chan = ret; ++ ++ ret = request_irq(host_priv->dma_irq, sdhci_bcm2708_dma_irq, ++ IRQF_SHARED, DRIVER_NAME " (dma)", host); ++ if (ret) { ++ dev_err(&pdev->dev, "cannot set DMA IRQ\n"); ++ goto err_add_dma_irq; ++ } ++ DBG("DMA CBs %p handle %08X DMA%d %p DMA IRQ %d\n", ++ host_priv->cb_base, (unsigned)host_priv->cb_handle, ++ host_priv->dma_chan, host_priv->dma_chan_base, ++ host_priv->dma_irq); ++#endif ++ ++ ret = sdhci_add_host(host); ++ if (ret) ++ goto err_add_host; ++ ++ platform_set_drvdata(pdev, host); ++ ret = device_create_file(&pdev->dev, &dev_attr_use_dma); ++ ret = device_create_file(&pdev->dev, &dev_attr_dma_wait); ++ ret = device_create_file(&pdev->dev, &dev_attr_status); ++ ++ printk(KERN_INFO "%s: BCM2708 SDHC host at 0x%08llx DMA %d IRQ %d\n", ++ mmc_hostname(host->mmc), (unsigned long long)iomem->start, ++ host_priv->dma_chan, host_priv->dma_irq); ++ ++ return 0; ++ ++err_add_host: ++#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA ++ free_irq(host_priv->dma_irq, host); ++err_add_dma_irq: ++ bcm_dma_chan_free(host_priv->dma_chan); ++err_add_dma: ++ dma_free_writecombine(&pdev->dev, SZ_4K, host_priv->cb_base, ++ host_priv->cb_handle); ++err_alloc_cb: ++#endif ++ bcm_power_close(host_priv->power_handle); ++err_power: ++ iounmap(host->ioaddr); ++err_remap: ++ release_mem_region(iomem->start, resource_size(iomem)); ++err_request: ++ sdhci_free_host(host); ++err: ++ dev_err(&pdev->dev, "probe failed, err %d\n", ret); ++ return ret; ++} ++ ++static int __devexit sdhci_bcm2708_remove(struct platform_device *pdev) ++{ ++ struct sdhci_host *host = platform_get_drvdata(pdev); ++ struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0); ++ struct sdhci_bcm2708_priv *host_priv = SDHCI_HOST_PRIV(host); ++ int dead; ++ u32 scratch; ++ ++ dead = 0; ++ scratch = sdhci_bcm2708_readl(host, SDHCI_INT_STATUS); ++ if (scratch == (u32)-1) ++ dead = 1; ++ ++ device_remove_file(&pdev->dev, &dev_attr_status); ++ device_remove_file(&pdev->dev, &dev_attr_dma_wait); ++ device_remove_file(&pdev->dev, &dev_attr_use_dma); ++ ++#ifdef CONFIG_MMC_SDHCI_BCM2708_DMA ++ free_irq(host_priv->dma_irq, host); ++ dma_free_writecombine(&pdev->dev, SZ_4K, host_priv->cb_base, ++ host_priv->cb_handle); ++#endif ++ bcm_power_close(host_priv->power_handle); ++ sdhci_remove_host(host, dead); ++ iounmap(host->ioaddr); ++ release_mem_region(iomem->start, resource_size(iomem)); ++ sdhci_free_host(host); ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++static struct platform_driver sdhci_bcm2708_driver = { ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++ .probe = sdhci_bcm2708_probe, ++ .remove = __devexit_p(sdhci_bcm2708_remove), ++ ++#ifdef CONFIG_PM ++ .suspend = sdhci_bcm2708_suspend, ++ .resume = sdhci_bcm2708_resume, ++#endif ++ ++}; ++ ++/*****************************************************************************\ ++ * * ++ * Driver init/exit * ++ * * ++\*****************************************************************************/ ++ ++static int __init sdhci_drv_init(void) ++{ ++ return platform_driver_register(&sdhci_bcm2708_driver); ++} ++ ++static void __exit sdhci_drv_exit(void) ++{ ++ platform_driver_unregister(&sdhci_bcm2708_driver); ++} ++ ++module_init(sdhci_drv_init); ++module_exit(sdhci_drv_exit); ++ ++MODULE_DESCRIPTION("Secure Digital Host Controller Interface platform driver"); ++MODULE_AUTHOR("Broadcom <info@broadcom.com>"); ++MODULE_LICENSE("GPL v2"); ++MODULE_ALIAS("platform:"DRIVER_NAME); ++ +--- a/drivers/mmc/host/sdhci.c ++++ b/drivers/mmc/host/sdhci.c +@@ -27,6 +27,7 @@ + + #include <linux/mmc/mmc.h> + #include <linux/mmc/host.h> ++#include <linux/mmc/sd.h> + + #include "sdhci.h" + +@@ -66,6 +67,7 @@ static inline int sdhci_runtime_pm_put(s + } + #endif + ++ + static void sdhci_dumpregs(struct sdhci_host *host) + { + pr_debug(DRIVER_NAME ": =========== REGISTER DUMP (%s)===========\n", +@@ -150,6 +152,9 @@ static void sdhci_set_card_detection(str + (host->mmc->caps & MMC_CAP_NONREMOVABLE)) + return; + ++ if (host->quirks2 & SDHCI_QUIRK2_OWN_CARD_DETECTION) ++ return; ++ + present = sdhci_readl(host, SDHCI_PRESENT_STATE) & + SDHCI_CARD_PRESENT; + irqs = present ? SDHCI_INT_CARD_REMOVE : SDHCI_INT_CARD_INSERT; +@@ -299,7 +304,7 @@ static void sdhci_read_block_pio(struct + u32 uninitialized_var(scratch); + u8 *buf; + +- DBG("PIO reading\n"); ++ DBG("PIO reading %db\n", host->data->blksz); + + blksize = host->data->blksz; + chunk = 0; +@@ -344,7 +349,7 @@ static void sdhci_write_block_pio(struct + u32 scratch; + u8 *buf; + +- DBG("PIO writing\n"); ++ DBG("PIO writing %db\n", host->data->blksz); + + blksize = host->data->blksz; + chunk = 0; +@@ -383,19 +388,28 @@ static void sdhci_write_block_pio(struct + local_irq_restore(flags); + } + +-static void sdhci_transfer_pio(struct sdhci_host *host) ++static void sdhci_transfer_pio(struct sdhci_host *host, u32 intstate) + { + u32 mask; ++ u32 state = 0; ++ u32 intmask; ++ int available; + + BUG_ON(!host->data); + + if (host->blocks == 0) + return; + +- if (host->data->flags & MMC_DATA_READ) ++ if (host->data->flags & MMC_DATA_READ) { + mask = SDHCI_DATA_AVAILABLE; +- else ++ intmask = SDHCI_INT_DATA_AVAIL; ++ } else { + mask = SDHCI_SPACE_AVAILABLE; ++ intmask = SDHCI_INT_SPACE_AVAIL; ++ } ++ ++ /* initially we can see whether we can procede using intstate */ ++ available = (intstate & intmask); + + /* + * Some controllers (JMicron JMB38x) mess up the buffer bits +@@ -406,7 +420,7 @@ static void sdhci_transfer_pio(struct sd + (host->data->blocks == 1)) + mask = ~0; + +- while (sdhci_readl(host, SDHCI_PRESENT_STATE) & mask) { ++ while (available) { + if (host->quirks & SDHCI_QUIRK_PIO_NEEDS_DELAY) + udelay(100); + +@@ -418,9 +432,11 @@ static void sdhci_transfer_pio(struct sd + host->blocks--; + if (host->blocks == 0) + break; ++ state = sdhci_readl(host, SDHCI_PRESENT_STATE); ++ available = state & mask; + } + +- DBG("PIO transfer complete.\n"); ++ DBG("PIO transfer complete - %d blocks left.\n", host->blocks); + } + + static char *sdhci_kmap_atomic(struct scatterlist *sg, unsigned long *flags) +@@ -693,7 +709,9 @@ static void sdhci_set_transfer_irqs(stru + u32 pio_irqs = SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL; + u32 dma_irqs = SDHCI_INT_DMA_END | SDHCI_INT_ADMA_ERROR; + +- if (host->flags & SDHCI_REQ_USE_DMA) ++ /* platform DMA will begin on receipt of PIO irqs */ ++ if ((host->flags & SDHCI_REQ_USE_DMA) && ++ !(host->flags & SDHCI_USE_PLATDMA)) + sdhci_clear_set_irqs(host, pio_irqs, dma_irqs); + else + sdhci_clear_set_irqs(host, dma_irqs, pio_irqs); +@@ -725,44 +743,25 @@ static void sdhci_prepare_data(struct sd + host->data_early = 0; + host->data->bytes_xfered = 0; + +- if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) ++ if (host->flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA | SDHCI_USE_PLATDMA)) + host->flags |= SDHCI_REQ_USE_DMA; + + /* + * FIXME: This doesn't account for merging when mapping the + * scatterlist. + */ +- if (host->flags & SDHCI_REQ_USE_DMA) { +- int broken, i; +- struct scatterlist *sg; +- +- broken = 0; +- if (host->flags & SDHCI_USE_ADMA) { +- if (host->quirks & SDHCI_QUIRK_32BIT_ADMA_SIZE) +- broken = 1; +- } else { +- if (host->quirks & SDHCI_QUIRK_32BIT_DMA_SIZE) +- broken = 1; +- } +- +- if (unlikely(broken)) { +- for_each_sg(data->sg, sg, data->sg_len, i) { +- if (sg->length & 0x3) { +- DBG("Reverting to PIO because of " +- "transfer size (%d)\n", +- sg->length); +- host->flags &= ~SDHCI_REQ_USE_DMA; +- break; +- } +- } +- } +- } + + /* + * The assumption here being that alignment is the same after + * translation to device address space. + */ +- if (host->flags & SDHCI_REQ_USE_DMA) { ++ if ((host->flags & (SDHCI_REQ_USE_DMA | SDHCI_USE_PLATDMA)) == ++ (SDHCI_REQ_USE_DMA | SDHCI_USE_PLATDMA)) { ++ ++ if (! sdhci_platdma_dmaable(host, data)) ++ host->flags &= ~SDHCI_REQ_USE_DMA; ++ ++ } else if (host->flags & SDHCI_REQ_USE_DMA) { + int broken, i; + struct scatterlist *sg; + +@@ -821,7 +820,8 @@ static void sdhci_prepare_data(struct sd + */ + WARN_ON(1); + host->flags &= ~SDHCI_REQ_USE_DMA; +- } else { ++ } else ++ if (!(host->flags & SDHCI_USE_PLATDMA)) { + WARN_ON(sg_cnt != 1); + sdhci_writel(host, sg_dma_address(data->sg), + SDHCI_DMA_ADDRESS); +@@ -837,11 +837,13 @@ static void sdhci_prepare_data(struct sd + if (host->version >= SDHCI_SPEC_200) { + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); + ctrl &= ~SDHCI_CTRL_DMA_MASK; +- if ((host->flags & SDHCI_REQ_USE_DMA) && +- (host->flags & SDHCI_USE_ADMA)) +- ctrl |= SDHCI_CTRL_ADMA32; +- else +- ctrl |= SDHCI_CTRL_SDMA; ++ if (! (host->flags & SDHCI_USE_PLATDMA)) { ++ if ((host->flags & SDHCI_REQ_USE_DMA) && ++ (host->flags & SDHCI_USE_ADMA)) ++ ctrl |= SDHCI_CTRL_ADMA32; ++ else ++ ctrl |= SDHCI_CTRL_SDMA; ++ } + sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + } + +@@ -893,7 +895,8 @@ static void sdhci_set_transfer_mode(stru + + if (data->flags & MMC_DATA_READ) + mode |= SDHCI_TRNS_READ; +- if (host->flags & SDHCI_REQ_USE_DMA) ++ if ((host->flags & SDHCI_REQ_USE_DMA) && ++ !(host->flags & SDHCI_USE_PLATDMA)) + mode |= SDHCI_TRNS_DMA; + + sdhci_writew(host, mode, SDHCI_TRANSFER_MODE); +@@ -909,13 +912,16 @@ static void sdhci_finish_data(struct sdh + host->data = NULL; + + if (host->flags & SDHCI_REQ_USE_DMA) { +- if (host->flags & SDHCI_USE_ADMA) +- sdhci_adma_table_post(host, data); +- else { ++ /* we may have to abandon an ongoing platform DMA */ ++ if (host->flags & SDHCI_USE_PLATDMA) ++ sdhci_platdma_reset(host, data); ++ ++ if (host->flags & (SDHCI_USE_PLATDMA | SDHCI_USE_SDMA)) { + dma_unmap_sg(mmc_dev(host->mmc), data->sg, + data->sg_len, (data->flags & MMC_DATA_READ) ? + DMA_FROM_DEVICE : DMA_TO_DEVICE); +- } ++ } else if (host->flags & SDHCI_USE_ADMA) ++ sdhci_adma_table_post(host, data); + } + + /* +@@ -985,10 +991,16 @@ static void sdhci_send_command(struct sd + timeout--; + mdelay(1); + } ++ DBG("send cmd %d - wait 0x%X irq 0x%x\n", cmd->opcode, mask, ++ sdhci_readl(host, SDHCI_INT_STATUS)); + + mod_timer(&host->timer, jiffies + 10 * HZ); + + host->cmd = cmd; ++ if (host->last_cmdop == MMC_APP_CMD) ++ host->last_cmdop = -cmd->opcode; ++ else ++ host->last_cmdop = cmd->opcode; + + sdhci_prepare_data(host, cmd); + +@@ -1234,6 +1246,35 @@ static int sdhci_set_power(struct sdhci_ + return power; + } + ++/* Power on or off the circuitary supporting the register set */ ++static int sdhci_set_plat_power(struct sdhci_host *host, int power_mode) ++{ ++ if (host->ops->set_plat_power) ++ return host->ops->set_plat_power(host, power_mode); ++ else ++ return 0; ++} ++ ++/* Click forwards one step towards fully on */ ++static int sdhci_enable(struct mmc_host *mmc) ++{ ++ struct sdhci_host *host; ++ ++ host = mmc_priv(mmc); ++ ++ return host->ops->enable? host->ops->enable(host): 0; ++} ++ ++/* Click backwards one step towards fully off */ ++static int sdhci_disable(struct mmc_host *mmc, int lazy) ++{ ++ struct sdhci_host *host; ++ ++ host = mmc_priv(mmc); ++ ++ return host->ops->disable? host->ops->disable(host, lazy): 0; ++} ++ + /*****************************************************************************\ + * * + * MMC callbacks * +@@ -1250,6 +1291,8 @@ static void sdhci_request(struct mmc_hos + + sdhci_runtime_pm_get(host); + ++ sdhci_runtime_pm_get(host); ++ + spin_lock_irqsave(&host->lock, flags); + + WARN_ON(host->mrq != NULL); +@@ -1314,6 +1357,7 @@ static void sdhci_do_set_ios(struct sdhc + { + unsigned long flags; + int vdd_bit = -1; ++ int rc; + u8 ctrl; + + spin_lock_irqsave(&host->lock, flags); +@@ -1383,7 +1427,7 @@ static void sdhci_do_set_ios(struct sdhc + else + ctrl &= ~SDHCI_CTRL_HISPD; + +- if (host->version >= SDHCI_SPEC_300) { ++ if (host->version >= SDHCI_SPEC_300 && !(host->ops->uhs_broken)) { + u16 clk, ctrl_2; + unsigned int clock; + +@@ -1474,6 +1518,12 @@ static void sdhci_do_set_ios(struct sdhc + + mmiowb(); + spin_unlock_irqrestore(&host->lock, flags); ++ ++ if (ios->power_mode == MMC_POWER_OFF) { ++ do ++ rc = sdhci_set_plat_power(host, ios->power_mode); ++ while (rc == -EINTR); ++ } + } + + static void sdhci_set_ios(struct mmc_host *mmc, struct mmc_ios *ios) +@@ -1934,6 +1984,8 @@ static const struct mmc_host_ops sdhci_o + .start_signal_voltage_switch = sdhci_start_signal_voltage_switch, + .execute_tuning = sdhci_execute_tuning, + .enable_preset_value = sdhci_enable_preset_value, ++ .enable = sdhci_enable, ++ .disable = sdhci_disable, + }; + + /*****************************************************************************\ +@@ -2091,10 +2143,13 @@ static void sdhci_cmd_irq(struct sdhci_h + BUG_ON(intmask == 0); + + if (!host->cmd) { +- pr_err("%s: Got command interrupt 0x%08x even " +- "though no command operation was in progress.\n", +- mmc_hostname(host->mmc), (unsigned)intmask); +- sdhci_dumpregs(host); ++ if (!(host->ops->extra_ints)) { ++ pr_err("%s: Got command interrupt 0x%08x even " ++ "though no command operation was in progress.\n", ++ mmc_hostname(host->mmc), (unsigned)intmask); ++ sdhci_dumpregs(host); ++ } else ++ DBG("cmd irq 0x%08x cmd complete\n", (unsigned)intmask); + return; + } + +@@ -2164,6 +2219,19 @@ static void sdhci_show_adma_error(struct + static void sdhci_show_adma_error(struct sdhci_host *host) { } + #endif + ++static void sdhci_data_end(struct sdhci_host *host) ++{ ++ if (host->cmd) { ++ /* ++ * Data managed to finish before the ++ * command completed. Make sure we do ++ * things in the proper order. ++ */ ++ host->data_early = 1; ++ } else ++ sdhci_finish_data(host); ++} ++ + static void sdhci_data_irq(struct sdhci_host *host, u32 intmask) + { + u32 command; +@@ -2192,35 +2260,57 @@ static void sdhci_data_irq(struct sdhci_ + return; + } + } +- +- pr_err("%s: Got data interrupt 0x%08x even " +- "though no data operation was in progress.\n", +- mmc_hostname(host->mmc), (unsigned)intmask); +- sdhci_dumpregs(host); +- +- return; +- } +- +- if (intmask & SDHCI_INT_DATA_TIMEOUT) +- host->data->error = -ETIMEDOUT; +- else if (intmask & SDHCI_INT_DATA_END_BIT) +- host->data->error = -EILSEQ; +- else if ((intmask & SDHCI_INT_DATA_CRC) && +- SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) +- != MMC_BUS_TEST_R) +- host->data->error = -EILSEQ; +- else if (intmask & SDHCI_INT_ADMA_ERROR) { ++ ++ if (!(host->ops->extra_ints)) { ++ pr_err("%s: Got data interrupt 0x%08x even " ++ "though no data operation was in progress.\n", ++ mmc_hostname(host->mmc), (unsigned)intmask); ++ sdhci_dumpregs(host); ++ } else ++ DBG("data irq 0x%08x but no data\n", (unsigned)intmask); ++ ++ return; ++ } ++ ++ if (intmask & SDHCI_INT_DATA_TIMEOUT) ++ host->data->error = -ETIMEDOUT; ++ else if (intmask & SDHCI_INT_DATA_END_BIT) { ++ DBG("end error in cmd %d\n", host->last_cmdop); ++ if (host->ops->spurious_crc_acmd51 && ++ host->last_cmdop == -SD_APP_SEND_SCR) { ++ DBG("ignoring spurious data_end_bit error\n"); ++ intmask = SDHCI_INT_DATA_AVAIL|SDHCI_INT_DATA_END; ++ } else ++ host->data->error = -EILSEQ; ++ } else if ((intmask & SDHCI_INT_DATA_CRC) && ++ SDHCI_GET_CMD(sdhci_readw(host, SDHCI_COMMAND)) ++ != MMC_BUS_TEST_R) { ++ DBG("crc error in cmd %d\n", host->last_cmdop); ++ if (host->ops->spurious_crc_acmd51 && ++ host->last_cmdop == -SD_APP_SEND_SCR) { ++ DBG("ignoring spurious data_crc_bit error\n"); ++ intmask = SDHCI_INT_DATA_AVAIL|SDHCI_INT_DATA_END; ++ } else ++ host->data->error = -EILSEQ; ++ } else if (intmask & SDHCI_INT_ADMA_ERROR) { + pr_err("%s: ADMA error\n", mmc_hostname(host->mmc)); +- sdhci_show_adma_error(host); +- host->data->error = -EIO; +- } +- +- if (host->data->error) +- sdhci_finish_data(host); +- else { +- if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) +- sdhci_transfer_pio(host); +- ++ sdhci_show_adma_error(host); ++ host->data->error = -EIO; ++ } ++ ++ if (host->data->error) { ++ DBG("finish request early on error %d\n", host->data->error); ++ sdhci_finish_data(host); ++ } else { ++ if (intmask & (SDHCI_INT_DATA_AVAIL | SDHCI_INT_SPACE_AVAIL)) { ++ if (host->flags & SDHCI_REQ_USE_DMA) { ++ /* possible only in PLATDMA mode */ ++ sdhci_platdma_avail(host, &intmask, ++ &sdhci_data_end); ++ } else ++ sdhci_transfer_pio(host, intmask); ++ } ++ + /* + * We currently don't do anything fancy with DMA + * boundaries, but as we can't disable the feature +@@ -2249,16 +2339,7 @@ static void sdhci_data_irq(struct sdhci_ + } + + if (intmask & SDHCI_INT_DATA_END) { +- if (host->cmd) { +- /* +- * Data managed to finish before the +- * command completed. Make sure we do +- * things in the proper order. +- */ +- host->data_early = 1; +- } else { +- sdhci_finish_data(host); +- } ++ sdhci_data_end(host); + } + } + } +@@ -2314,6 +2395,22 @@ static irqreturn_t sdhci_irq(int irq, vo + tasklet_schedule(&host->card_tasklet); + } + ++ if (intmask & SDHCI_INT_ERROR_MASK & ~SDHCI_INT_ERROR) ++ DBG("controller reports error 0x%x -" ++ "%s%s%s%s%s%s%s%s%s%s", ++ intmask, ++ intmask & SDHCI_INT_TIMEOUT? " timeout": "", ++ intmask & SDHCI_INT_CRC ? " crc": "", ++ intmask & SDHCI_INT_END_BIT? " endbit": "", ++ intmask & SDHCI_INT_INDEX? " index": "", ++ intmask & SDHCI_INT_DATA_TIMEOUT? " data_timeout": "", ++ intmask & SDHCI_INT_DATA_CRC? " data_crc": "", ++ intmask & SDHCI_INT_DATA_END_BIT? " data_endbit": "", ++ intmask & SDHCI_INT_BUS_POWER? " buspower": "", ++ intmask & SDHCI_INT_ACMD12ERR? " acmd12": "", ++ intmask & SDHCI_INT_ADMA_ERROR? " adma": "" ++ ); ++ + if (intmask & SDHCI_INT_CMD_MASK) { + sdhci_writel(host, intmask & SDHCI_INT_CMD_MASK, + SDHCI_INT_STATUS); +@@ -2535,6 +2632,90 @@ EXPORT_SYMBOL_GPL(sdhci_runtime_resume_h + + #endif + ++#ifdef CONFIG_PM_RUNTIME ++ ++static int sdhci_runtime_pm_get(struct sdhci_host *host) ++{ ++ return pm_runtime_get_sync(host->mmc->parent); ++} ++ ++static int sdhci_runtime_pm_put(struct sdhci_host *host) ++{ ++ pm_runtime_mark_last_busy(host->mmc->parent); ++ return pm_runtime_put_autosuspend(host->mmc->parent); ++} ++ ++int sdhci_runtime_suspend_host(struct sdhci_host *host) ++{ ++ unsigned long flags; ++ int ret = 0; ++ ++ /* Disable tuning since we are suspending */ ++ if (host->version >= SDHCI_SPEC_300 && ++ host->tuning_mode == SDHCI_TUNING_MODE_1) { ++ del_timer_sync(&host->tuning_timer); ++ host->flags &= ~SDHCI_NEEDS_RETUNING; ++ } ++ ++ spin_lock_irqsave(&host->lock, flags); ++ sdhci_mask_irqs(host, SDHCI_INT_ALL_MASK); ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ synchronize_irq(host->irq); ++ ++ spin_lock_irqsave(&host->lock, flags); ++ host->runtime_suspended = true; ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(sdhci_runtime_suspend_host); ++ ++int sdhci_runtime_resume_host(struct sdhci_host *host) ++{ ++ unsigned long flags; ++ int ret = 0, host_flags = host->flags; ++ ++ if (host_flags & (SDHCI_USE_SDMA | SDHCI_USE_ADMA)) { ++ if (host->ops->enable_dma) ++ host->ops->enable_dma(host); ++ } ++ ++ sdhci_init(host, 0); ++ ++ /* Force clock and power re-program */ ++ host->pwr = 0; ++ host->clock = 0; ++ sdhci_do_set_ios(host, &host->mmc->ios); ++ ++ sdhci_do_start_signal_voltage_switch(host, &host->mmc->ios); ++ if (host_flags & SDHCI_PV_ENABLED) ++ sdhci_do_enable_preset_value(host, true); ++ ++ /* Set the re-tuning expiration flag */ ++ if ((host->version >= SDHCI_SPEC_300) && host->tuning_count && ++ (host->tuning_mode == SDHCI_TUNING_MODE_1)) ++ host->flags |= SDHCI_NEEDS_RETUNING; ++ ++ spin_lock_irqsave(&host->lock, flags); ++ ++ host->runtime_suspended = false; ++ ++ /* Enable SDIO IRQ */ ++ if ((host->flags & SDHCI_SDIO_IRQ_ENABLED)) ++ sdhci_enable_sdio_irq_nolock(host, true); ++ ++ /* Enable Card Detection */ ++ sdhci_enable_card_detection(host); ++ ++ spin_unlock_irqrestore(&host->lock, flags); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(sdhci_runtime_resume_host); ++ ++#endif ++ + /*****************************************************************************\ + * * + * Device allocation/registration * +@@ -2738,7 +2919,7 @@ int sdhci_add_host(struct sdhci_host *ho + /* Auto-CMD23 stuff only works in ADMA or PIO. */ + if ((host->version >= SDHCI_SPEC_300) && + ((host->flags & SDHCI_USE_ADMA) || +- !(host->flags & SDHCI_USE_SDMA))) { ++ !(host->flags & SDHCI_USE_SDMA) )) { + host->flags |= SDHCI_AUTO_CMD23; + DBG("%s: Auto-CMD23 available\n", mmc_hostname(mmc)); + } else { +@@ -2800,6 +2981,15 @@ int sdhci_add_host(struct sdhci_host *ho + else + mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE; + ++ /* ++ * If Power Off Notify capability is enabled by the host, ++ * set notify to short power off notify timeout value. ++ */ ++ if (mmc->caps2 & MMC_CAP2_POWEROFF_NOTIFY) ++ mmc->power_notify_type = MMC_HOST_PW_NOTIFY_SHORT; ++ else ++ mmc->power_notify_type = MMC_HOST_PW_NOTIFY_NONE; ++ + /* Initial value for re-tuning timer count */ + host->tuning_count = (caps[1] & SDHCI_RETUNING_TIMER_COUNT_MASK) >> + SDHCI_RETUNING_TIMER_COUNT_SHIFT; +@@ -2875,6 +3065,9 @@ int sdhci_add_host(struct sdhci_host *ho + mmc->caps |= MMC_CAP_MAX_CURRENT_200; + } + ++ if(host->ops->voltage_broken) ++ ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31; ++ + mmc->ocr_avail = ocr_avail; + mmc->ocr_avail_sdio = ocr_avail; + if (host->ocr_avail_sdio) +--- a/drivers/mmc/host/sdhci.h ++++ b/drivers/mmc/host/sdhci.h +@@ -274,6 +274,24 @@ struct sdhci_ops { + void (*platform_reset_enter)(struct sdhci_host *host, u8 mask); + void (*platform_reset_exit)(struct sdhci_host *host, u8 mask); + int (*set_uhs_signaling)(struct sdhci_host *host, unsigned int uhs); ++ ++ int (*enable)(struct sdhci_host *mmc); ++ int (*disable)(struct sdhci_host *mmc, int lazy); ++ int (*set_plat_power)(struct sdhci_host *mmc, ++ int power_mode); ++ ++ int (*pdma_able)(struct sdhci_host *host, ++ struct mmc_data *data); ++ void (*pdma_avail)(struct sdhci_host *host, ++ unsigned int *ref_intmask, ++ void(*complete)(struct sdhci_host *)); ++ void (*pdma_reset)(struct sdhci_host *host, ++ struct mmc_data *data); ++ unsigned int (*extra_ints)(struct sdhci_host *host); ++ unsigned int (*spurious_crc_acmd51)(struct sdhci_host *host); ++ unsigned int (*voltage_broken)(struct sdhci_host *host); ++ unsigned int (*uhs_broken)(struct sdhci_host *host); ++ + void (*hw_reset)(struct sdhci_host *host); + }; + +@@ -383,6 +401,34 @@ extern void sdhci_enable_irq_wakeups(str + #ifdef CONFIG_PM_RUNTIME + extern int sdhci_runtime_suspend_host(struct sdhci_host *host); + extern int sdhci_runtime_resume_host(struct sdhci_host *host); ++#endif ++ ++static inline int /*bool*/ ++sdhci_platdma_dmaable(struct sdhci_host *host, struct mmc_data *data) ++{ ++ if (host->ops->pdma_able) ++ return host->ops->pdma_able(host, data); ++ else ++ return 1; ++} ++static inline void ++sdhci_platdma_avail(struct sdhci_host *host, unsigned int *ref_intmask, ++ void(*completion_callback)(struct sdhci_host *)) ++{ ++ if (host->ops->pdma_avail) ++ host->ops->pdma_avail(host, ref_intmask, completion_callback); ++} ++ ++static inline void ++sdhci_platdma_reset(struct sdhci_host *host, struct mmc_data *data) ++{ ++ if (host->ops->pdma_reset) ++ host->ops->pdma_reset(host, data); ++} ++ ++#ifdef CONFIG_PM_RUNTIME ++extern int sdhci_runtime_suspend_host(struct sdhci_host *host); ++extern int sdhci_runtime_resume_host(struct sdhci_host *host); + #endif + + #endif /* __SDHCI_HW_H */ +--- a/include/linux/mmc/sdhci.h ++++ b/include/linux/mmc/sdhci.h +@@ -90,6 +90,8 @@ struct sdhci_host { + + unsigned int quirks2; /* More deviations from spec. */ + ++#define SDHCI_QUIRK2_OWN_CARD_DETECTION (1<<0) ++ + int irq; /* Device IRQ */ + void __iomem *ioaddr; /* Mapped address */ + +@@ -120,6 +122,7 @@ struct sdhci_host { + #define SDHCI_PV_ENABLED (1<<8) /* Preset value enabled */ + #define SDHCI_SDIO_IRQ_ENABLED (1<<9) /* SDIO irq enabled */ + #define SDHCI_HS200_NEEDS_TUNING (1<<10) /* HS200 needs tuning */ ++#define SDHCI_USE_PLATDMA (1<<11) /* Host uses 3rd party DMA */ + + unsigned int version; /* SDHCI spec. version */ + +@@ -131,9 +134,9 @@ struct sdhci_host { + u8 pwr; /* Current voltage */ + + bool runtime_suspended; /* Host is runtime suspended */ +- + struct mmc_request *mrq; /* Current request */ + struct mmc_command *cmd; /* Current command */ ++ int last_cmdop; /* Opcode of last cmd sent */ + struct mmc_data *data; /* Current data request */ + unsigned int data_early:1; /* Data finished before cmd */ + diff --git a/target/linux/brcm2708/patches-3.3/0003-bcm2708-watchdog-driver.patch b/target/linux/brcm2708/patches-3.3/0003-bcm2708-watchdog-driver.patch new file mode 100644 index 0000000000..cde3eef4e2 --- /dev/null +++ b/target/linux/brcm2708/patches-3.3/0003-bcm2708-watchdog-driver.patch @@ -0,0 +1,436 @@ +From ed8c2d720954efc5a440912292ed11da2f50aaea Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Tue, 17 Jan 2012 19:20:57 +0000 +Subject: [PATCH 3/7] bcm2708 watchdog driver + +Signed-off-by: popcornmix <popcornmix@gmail.com> +--- + drivers/watchdog/Kconfig | 6 + + drivers/watchdog/Makefile | 1 + + drivers/watchdog/bcm2708_wdog.c | 385 +++++++++++++++++++++++++++++++++++++++ + 3 files changed, 392 insertions(+), 0 deletions(-) + create mode 100644 drivers/watchdog/bcm2708_wdog.c + +diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig +index 86b0735..7675ebc 100644 +--- a/drivers/watchdog/Kconfig ++++ b/drivers/watchdog/Kconfig +@@ -348,6 +348,12 @@ config IMX2_WDT + To compile this driver as a module, choose M here: the + module will be called imx2_wdt. + ++config BCM2708_WDT ++ tristate "BCM2708 Watchdog" ++ depends on ARCH_BCM2708 ++ help ++ Enables BCM2708 watchdog support. ++ + # AVR32 Architecture + + config AT32AP700X_WDT +diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile +index 55bd574..803f0bc 100644 +--- a/drivers/watchdog/Makefile ++++ b/drivers/watchdog/Makefile +@@ -54,6 +54,7 @@ obj-$(CONFIG_NUC900_WATCHDOG) += nuc900_wdt.o + obj-$(CONFIG_ADX_WATCHDOG) += adx_wdt.o + obj-$(CONFIG_TS72XX_WATCHDOG) += ts72xx_wdt.o + obj-$(CONFIG_IMX2_WDT) += imx2_wdt.o ++obj-$(CONFIG_BCM2708_WDT) += bcm2708_wdog.o + + # AVR32 Architecture + obj-$(CONFIG_AT32AP700X_WDT) += at32ap700x_wdt.o +diff --git a/drivers/watchdog/bcm2708_wdog.c b/drivers/watchdog/bcm2708_wdog.c +new file mode 100644 +index 0000000..dd33c35 +--- /dev/null ++++ b/drivers/watchdog/bcm2708_wdog.c +@@ -0,0 +1,385 @@ ++/* ++ * Broadcom BCM2708 watchdog driver. ++ * ++ * (c) Copyright 2010 Broadcom Europe Ltd ++ * ++ * 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. ++ * ++ * BCM2708 watchdog driver. Loosely based on wdt driver. ++ */ ++ ++#include <linux/interrupt.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/types.h> ++#include <linux/miscdevice.h> ++#include <linux/watchdog.h> ++#include <linux/fs.h> ++#include <linux/ioport.h> ++#include <linux/notifier.h> ++#include <linux/reboot.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/uaccess.h> ++#include <mach/platform.h> ++ ++#include <asm/system.h> ++ ++#define SECS_TO_WDOG_TICKS(x) ((x) << 16) ++#define WDOG_TICKS_TO_SECS(x) ((x) >> 16) ++ ++static unsigned long wdog_is_open; ++static uint32_t wdog_ticks; /* Ticks to load into wdog timer */ ++static char expect_close; ++ ++/* ++ * Module parameters ++ */ ++ ++#define WD_TIMO 10 /* Default heartbeat = 60 seconds */ ++static int heartbeat = WD_TIMO; /* Heartbeat in seconds */ ++ ++module_param(heartbeat, int, 0); ++MODULE_PARM_DESC(heartbeat, ++ "Watchdog heartbeat in seconds. (0 < heartbeat < 65536, default=" ++ __MODULE_STRING(WD_TIMO) ")"); ++ ++static int nowayout = WATCHDOG_NOWAYOUT; ++module_param(nowayout, int, 0); ++MODULE_PARM_DESC(nowayout, ++ "Watchdog cannot be stopped once started (default=" ++ __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); ++ ++static DEFINE_SPINLOCK(wdog_lock); ++ ++/** ++ * Start the watchdog driver. ++ */ ++ ++static int wdog_start(unsigned long timeout) ++{ ++ uint32_t cur; ++ unsigned long flags; ++ spin_lock_irqsave(&wdog_lock, flags); ++ ++ /* enable the watchdog */ ++ iowrite32(PM_PASSWORD | (timeout & PM_WDOG_TIME_SET), ++ __io_address(PM_WDOG)); ++ cur = ioread32(__io_address(PM_RSTC)); ++ iowrite32(PM_PASSWORD | (cur & PM_RSTC_WRCFG_CLR) | ++ PM_RSTC_WRCFG_FULL_RESET, __io_address(PM_RSTC)); ++ ++ spin_unlock_irqrestore(&wdog_lock, flags); ++ return 0; ++} ++ ++/** ++ * Stop the watchdog driver. ++ */ ++ ++static int wdog_stop(void) ++{ ++ iowrite32(PM_PASSWORD | PM_RSTC_RESET, __io_address(PM_RSTC)); ++ printk(KERN_INFO "watchdog stopped\n"); ++ return 0; ++} ++ ++/** ++ * Reload counter one with the watchdog heartbeat. We don't bother ++ * reloading the cascade counter. ++ */ ++ ++static void wdog_ping(void) ++{ ++ wdog_start(wdog_ticks); ++} ++ ++/** ++ * @t: the new heartbeat value that needs to be set. ++ * ++ * Set a new heartbeat value for the watchdog device. If the heartbeat ++ * value is incorrect we keep the old value and return -EINVAL. If ++ * successful we return 0. ++ */ ++ ++static int wdog_set_heartbeat(int t) ++{ ++ if (t < 1 || t > WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET)) ++ return -EINVAL; ++ ++ heartbeat = t; ++ wdog_ticks = SECS_TO_WDOG_TICKS(t); ++ return 0; ++} ++ ++/** ++ * @file: file handle to the watchdog ++ * @buf: buffer to write (unused as data does not matter here ++ * @count: count of bytes ++ * @ppos: pointer to the position to write. No seeks allowed ++ * ++ * A write to a watchdog device is defined as a keepalive signal. ++ * ++ * if 'nowayout' is set then normally a close() is ignored. But ++ * if you write 'V' first then the close() will stop the timer. ++ */ ++ ++static ssize_t wdog_write(struct file *file, const char __user *buf, ++ size_t count, loff_t *ppos) ++{ ++ if (count) { ++ if (!nowayout) { ++ size_t i; ++ ++ /* In case it was set long ago */ ++ expect_close = 0; ++ ++ for (i = 0; i != count; i++) { ++ char c; ++ if (get_user(c, buf + i)) ++ return -EFAULT; ++ if (c == 'V') ++ expect_close = 42; ++ } ++ } ++ wdog_ping(); ++ } ++ return count; ++} ++ ++static int wdog_get_status(void) ++{ ++ unsigned long flags; ++ int status = 0; ++ spin_lock_irqsave(&wdog_lock, flags); ++ /* FIXME: readback reset reason */ ++ spin_unlock_irqrestore(&wdog_lock, flags); ++ return status; ++} ++ ++static uint32_t wdog_get_remaining(void) ++{ ++ uint32_t ret = ioread32(__io_address(PM_WDOG)); ++ return ret & PM_WDOG_TIME_SET; ++} ++ ++/** ++ * @file: file handle to the device ++ * @cmd: watchdog command ++ * @arg: argument pointer ++ * ++ * The watchdog API defines a common set of functions for all watchdogs ++ * according to their available features. We only actually usefully support ++ * querying capabilities and current status. ++ */ ++ ++static long wdog_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ void __user *argp = (void __user *)arg; ++ int __user *p = argp; ++ int new_heartbeat; ++ int status; ++ int options; ++ uint32_t remaining; ++ ++ struct watchdog_info ident = { ++ .options = WDIOF_SETTIMEOUT| ++ WDIOF_MAGICCLOSE| ++ WDIOF_KEEPALIVEPING, ++ .firmware_version = 1, ++ .identity = "BCM2708", ++ }; ++ ++ switch (cmd) { ++ case WDIOC_GETSUPPORT: ++ return copy_to_user(argp, &ident, sizeof(ident)) ? -EFAULT : 0; ++ case WDIOC_GETSTATUS: ++ status = wdog_get_status(); ++ return put_user(status, p); ++ case WDIOC_GETBOOTSTATUS: ++ return put_user(0, p); ++ case WDIOC_KEEPALIVE: ++ wdog_ping(); ++ return 0; ++ case WDIOC_SETTIMEOUT: ++ if (get_user(new_heartbeat, p)) ++ return -EFAULT; ++ if (wdog_set_heartbeat(new_heartbeat)) ++ return -EINVAL; ++ wdog_ping(); ++ /* Fall */ ++ case WDIOC_GETTIMEOUT: ++ return put_user(heartbeat, p); ++ case WDIOC_GETTIMELEFT: ++ remaining = WDOG_TICKS_TO_SECS(wdog_get_remaining()); ++ return put_user(remaining, p); ++ case WDIOC_SETOPTIONS: ++ if (get_user(options, p)) ++ return -EFAULT; ++ if (options & WDIOS_DISABLECARD) ++ wdog_stop(); ++ if (options & WDIOS_ENABLECARD) ++ wdog_start(wdog_ticks); ++ return 0; ++ default: ++ return -ENOTTY; ++ } ++} ++ ++/** ++ * @inode: inode of device ++ * @file: file handle to device ++ * ++ * The watchdog device has been opened. The watchdog device is single ++ * open and on opening we load the counters. ++ */ ++ ++static int wdog_open(struct inode *inode, struct file *file) ++{ ++ if (test_and_set_bit(0, &wdog_is_open)) ++ return -EBUSY; ++ /* ++ * Activate ++ */ ++ wdog_start(wdog_ticks); ++ return nonseekable_open(inode, file); ++} ++ ++/** ++ * @inode: inode to board ++ * @file: file handle to board ++ * ++ * The watchdog has a configurable API. There is a religious dispute ++ * between people who want their watchdog to be able to shut down and ++ * those who want to be sure if the watchdog manager dies the machine ++ * reboots. In the former case we disable the counters, in the latter ++ * case you have to open it again very soon. ++ */ ++ ++static int wdog_release(struct inode *inode, struct file *file) ++{ ++ if (expect_close == 42) { ++ wdog_stop(); ++ } else { ++ printk(KERN_CRIT ++ "wdt: WDT device closed unexpectedly. WDT will not stop!\n"); ++ wdog_ping(); ++ } ++ clear_bit(0, &wdog_is_open); ++ expect_close = 0; ++ return 0; ++} ++ ++/** ++ * @this: our notifier block ++ * @code: the event being reported ++ * @unused: unused ++ * ++ * Our notifier is called on system shutdowns. Turn the watchdog ++ * off so that it does not fire during the next reboot. ++ */ ++ ++static int wdog_notify_sys(struct notifier_block *this, unsigned long code, ++ void *unused) ++{ ++ if (code == SYS_DOWN || code == SYS_HALT) ++ wdog_stop(); ++ return NOTIFY_DONE; ++} ++ ++/* ++ * Kernel Interfaces ++ */ ++ ++ ++static const struct file_operations wdog_fops = { ++ .owner = THIS_MODULE, ++ .llseek = no_llseek, ++ .write = wdog_write, ++ .unlocked_ioctl = wdog_ioctl, ++ .open = wdog_open, ++ .release = wdog_release, ++}; ++ ++static struct miscdevice wdog_miscdev = { ++ .minor = WATCHDOG_MINOR, ++ .name = "watchdog", ++ .fops = &wdog_fops, ++}; ++ ++/* ++ * The WDT card needs to learn about soft shutdowns in order to ++ * turn the timebomb registers off. ++ */ ++ ++static struct notifier_block wdog_notifier = { ++ .notifier_call = wdog_notify_sys, ++}; ++ ++/** ++ * cleanup_module: ++ * ++ * Unload the watchdog. You cannot do this with any file handles open. ++ * If your watchdog is set to continue ticking on close and you unload ++ * it, well it keeps ticking. We won't get the interrupt but the board ++ * will not touch PC memory so all is fine. You just have to load a new ++ * module in 60 seconds or reboot. ++ */ ++ ++static void __exit wdog_exit(void) ++{ ++ misc_deregister(&wdog_miscdev); ++ unregister_reboot_notifier(&wdog_notifier); ++} ++ ++static int __init wdog_init(void) ++{ ++ int ret; ++ ++ /* Check that the heartbeat value is within it's range; ++ if not reset to the default */ ++ if (wdog_set_heartbeat(heartbeat)) { ++ wdog_set_heartbeat(WD_TIMO); ++ printk(KERN_INFO "bcm2708_wdog: heartbeat value must be " ++ "0 < heartbeat < %d, using %d\n", ++ WDOG_TICKS_TO_SECS(PM_WDOG_TIME_SET), ++ WD_TIMO); ++ } ++ ++ ret = register_reboot_notifier(&wdog_notifier); ++ if (ret) { ++ printk(KERN_ERR ++ "wdt: cannot register reboot notifier (err=%d)\n", ret); ++ goto out_reboot; ++ } ++ ++ ret = misc_register(&wdog_miscdev); ++ if (ret) { ++ printk(KERN_ERR ++ "wdt: cannot register miscdev on minor=%d (err=%d)\n", ++ WATCHDOG_MINOR, ret); ++ goto out_misc; ++ } ++ ++ printk(KERN_INFO "bcm2708 watchdog, heartbeat=%d sec (nowayout=%d)\n", ++ heartbeat, nowayout); ++ return 0; ++ ++out_misc: ++ unregister_reboot_notifier(&wdog_notifier); ++out_reboot: ++ return ret; ++} ++ ++module_init(wdog_init); ++module_exit(wdog_exit); ++ ++MODULE_AUTHOR("Luke Diamand"); ++MODULE_DESCRIPTION("Driver for BCM2708 watchdog"); ++MODULE_ALIAS_MISCDEV(WATCHDOG_MINOR); ++MODULE_ALIAS_MISCDEV(TEMP_MINOR); ++MODULE_LICENSE("GPL"); ++ +-- +1.7.5.4 + diff --git a/target/linux/brcm2708/patches-3.3/0004-bcm2708-framebuffer-driver.patch b/target/linux/brcm2708/patches-3.3/0004-bcm2708-framebuffer-driver.patch new file mode 100644 index 0000000000..3b04bc0d04 --- /dev/null +++ b/target/linux/brcm2708/patches-3.3/0004-bcm2708-framebuffer-driver.patch @@ -0,0 +1,2990 @@ +From 4593b68d5da44244266737c3c18da81666fa6d15 Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Tue, 17 Jan 2012 19:21:26 +0000 +Subject: [PATCH 4/7] bcm2708 framebuffer driver + +Signed-off-by: popcornmix <popcornmix@gmail.com> +--- + drivers/video/Kconfig | 14 + + drivers/video/Makefile | 1 + + drivers/video/bcm2708_fb.c | 440 +++++ + drivers/video/logo/logo_linux_clut224.ppm | 2483 ++++++++++------------------- + 4 files changed, 1336 insertions(+), 1602 deletions(-) + create mode 100644 drivers/video/bcm2708_fb.c + +diff --git a/drivers/video/Kconfig b/drivers/video/Kconfig +index 549b960..f343042 100644 +--- a/drivers/video/Kconfig ++++ b/drivers/video/Kconfig +@@ -303,6 +303,20 @@ config FB_PM2_FIFO_DISCONNECT + help + Support the Permedia2 FIFO disconnect feature. + ++config FB_BCM2708 ++ tristate "BCM2708 framebuffer support" ++ depends on FB && ARM ++ select FB_CFB_FILLRECT ++ select FB_CFB_COPYAREA ++ select FB_CFB_IMAGEBLIT ++ help ++ This framebuffer device driver is for the BCM2708 framebuffer. ++ ++ If you want to compile this as a module (=code which can be ++ inserted into and removed from the running kernel), say M ++ here and read <file:Documentation/kbuild/modules.txt>. The module ++ will be called bcm2708_fb. ++ + config FB_ARMCLCD + tristate "ARM PrimeCell PL110 support" + depends on FB && ARM && ARM_AMBA +diff --git a/drivers/video/Makefile b/drivers/video/Makefile +index 8b83129..0c5cee3 100644 +--- a/drivers/video/Makefile ++++ b/drivers/video/Makefile +@@ -95,6 +95,7 @@ obj-$(CONFIG_FB_ATMEL) += atmel_lcdfb.o + obj-$(CONFIG_FB_PVR2) += pvr2fb.o + obj-$(CONFIG_FB_VOODOO1) += sstfb.o + obj-$(CONFIG_FB_ARMCLCD) += amba-clcd.o ++obj-$(CONFIG_FB_BCM2708) += bcm2708_fb.o + obj-$(CONFIG_FB_68328) += 68328fb.o + obj-$(CONFIG_FB_GBE) += gbefb.o + obj-$(CONFIG_FB_CIRRUS) += cirrusfb.o +diff --git a/drivers/video/bcm2708_fb.c b/drivers/video/bcm2708_fb.c +new file mode 100644 +index 0000000..6bfdeef +--- /dev/null ++++ b/drivers/video/bcm2708_fb.c +@@ -0,0 +1,440 @@ ++/* ++ * linux/drivers/video/bcm2708_fb.c ++ * ++ * Copyright (C) 2010 Broadcom ++ * ++ * This file is subject to the terms and conditions of the GNU General Public ++ * License. See the file COPYING in the main directory of this archive ++ * for more details. ++ * ++ * Broadcom simple framebuffer driver ++ */ ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/errno.h> ++#include <linux/string.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/mm.h> ++#include <linux/fb.h> ++#include <linux/init.h> ++#include <linux/ioport.h> ++#include <linux/list.h> ++#include <linux/platform_device.h> ++#include <linux/clk.h> ++ ++#include <mach/platform.h> ++#include <mach/vcio.h> ++ ++#include <asm/sizes.h> ++#include <linux/io.h> ++#include <linux/dma-mapping.h> ++ ++/* This is limited to 16 characters when displayed by X startup */ ++static const char *bcm2708_name = "BCM2708 FB"; ++ ++#define DRIVER_NAME "bcm2708_fb" ++ ++/* this data structure describes each frame buffer device we find */ ++ ++struct fbinfo_s { ++ int xres, yres, xres_virtual, yres_virtual; ++ int pitch, bpp; ++ int xoffset, yoffset; ++ int base; ++ int screen_size; ++}; ++ ++struct bcm2708_fb { ++ struct fb_info fb; ++ struct platform_device *dev; ++ void __iomem *regs; ++ volatile struct fbinfo_s *info; ++ dma_addr_t dma; ++ u32 cmap[16]; ++}; ++ ++#define to_bcm2708(info) container_of(info, struct bcm2708_fb, fb) ++ ++static int ++bcm2708_fb_set_bitfields(struct fb_var_screeninfo *var) ++{ ++ int ret = 0; ++ ++ memset(&var->transp, 0, sizeof(var->transp)); ++ ++ var->red.msb_right = 0; ++ var->green.msb_right = 0; ++ var->blue.msb_right = 0; ++ ++ switch (var->bits_per_pixel) { ++ case 1: ++ case 2: ++ case 4: ++ case 8: ++ var->red.length = var->bits_per_pixel; ++ var->red.offset = 0; ++ var->green.length = var->bits_per_pixel; ++ var->green.offset = 0; ++ var->blue.length = var->bits_per_pixel; ++ var->blue.offset = 0; ++ break; ++ case 16: ++ var->red.length = 5; ++ var->blue.length = 5; ++ /* ++ * Green length can be 5 or 6 depending whether ++ * we're operating in RGB555 or RGB565 mode. ++ */ ++ if (var->green.length != 5 && var->green.length != 6) ++ var->green.length = 6; ++ break; ++ case 32: ++ var->red.length = 8; ++ var->green.length = 8; ++ var->blue.length = 8; ++ break; ++ default: ++ ret = -EINVAL; ++ break; ++ } ++ ++ /* ++ * >= 16bpp displays have separate colour component bitfields ++ * encoded in the pixel data. Calculate their position from ++ * the bitfield length defined above. ++ */ ++ if (ret == 0 && var->bits_per_pixel >= 16) { ++ var->blue.offset = 0; ++ var->green.offset = var->blue.offset + var->blue.length; ++ var->red.offset = var->green.offset + var->green.length; ++ } ++ ++ return ret; ++} ++ ++static int bcm2708_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) ++{ ++ ++ // info input, var output ++ int yres; ++ /* memory size in pixels */ ++ unsigned pixels = info->screen_size * 8 / var->bits_per_pixel; ++ ++ // info input, var output ++ printk(KERN_ERR "bcm2708_fb_check_var info(%p) %dx%d (%dx%d), %d, %d\n", info, info->var.xres, info->var.yres, info->var.xres_virtual, info->var.yres_virtual, (int)info->screen_size, info->var.bits_per_pixel ); ++ printk(KERN_ERR "bcm2708_fb_check_var var(%p) %dx%d (%dx%d), %d, %d\n", var, var->xres, var->yres, var->xres_virtual, var->yres_virtual, var->bits_per_pixel, pixels); ++ ++ if (!var->bits_per_pixel) var->bits_per_pixel = 16; ++ ++ if (0 && var->bits_per_pixel != 16 && var->bits_per_pixel != 32) { ++ printk(KERN_ERR "bcm2708_fb_check_var: ERROR: bits_per_pixel=%d\n", var->bits_per_pixel); ++ return -EINVAL; ++ } ++ ++ bcm2708_fb_set_bitfields(var); ++ ++ if (var->xres_virtual < var->xres) ++ var->xres_virtual = var->xres; ++ /* use highest possible virtual resolution */ ++ if (var->yres_virtual == -1) { ++ var->yres_virtual = 480; //pixels / var->xres_virtual; ++ ++ printk(KERN_ERR ++ "bcm2708_fb_check_var: virtual resolution set to maximum of %dx%d\n", ++ var->xres_virtual, var->yres_virtual); ++ } ++ if (var->yres_virtual < var->yres) ++ var->yres_virtual = var->yres; ++ ++ #if 0 ++ if (var->xres_virtual * var->yres_virtual > pixels) { ++ printk(KERN_ERR "bcm2708_fb_check_var: mode %dx%dx%d rejected... " ++ "virtual resolution too high to fit into video memory!\n", ++ var->xres_virtual, var->yres_virtual, ++ var->bits_per_pixel); ++ return -EINVAL; ++ } ++ #endif ++ if (var->xoffset < 0) ++ var->xoffset = 0; ++ if (var->yoffset < 0) ++ var->yoffset = 0; ++ ++ /* truncate xoffset and yoffset to maximum if too high */ ++ if (var->xoffset > var->xres_virtual - var->xres) ++ var->xoffset = var->xres_virtual - var->xres - 1; ++ if (var->yoffset > var->yres_virtual - var->yres) ++ var->yoffset = var->yres_virtual - var->yres - 1; ++ ++ var->red.msb_right = ++ var->green.msb_right = ++ var->blue.msb_right = ++ var->transp.offset = ++ var->transp.length = ++ var->transp.msb_right = 0; ++ ++ yres = var->yres; ++ if (var->vmode & FB_VMODE_DOUBLE) ++ yres *= 2; ++ else if (var->vmode & FB_VMODE_INTERLACED) ++ yres = (yres + 1) / 2; ++ ++ if (yres > 1200) { ++ printk(KERN_ERR "bcm2708_fb_check_var: ERROR: VerticalTotal >= 1200; " ++ "special treatment required! (TODO)\n"); ++ return -EINVAL; ++ } ++ ++ //if (cirrusfb_check_pixclock(var, info)) ++ // return -EINVAL; ++ ++ //if (!is_laguna(cinfo)) ++ // var->accel_flags = FB_ACCELF_TEXT; ++ ++ return 0; ++} ++ ++static int bcm2708_fb_set_par(struct fb_info *info) ++{ ++ unsigned val = 0; ++ struct bcm2708_fb *fb = to_bcm2708(info); ++ volatile struct fbinfo_s *fbinfo = fb->info; ++ fbinfo->xres = info->var.xres; ++ fbinfo->yres = info->var.yres; ++ fbinfo->xres_virtual = info->var.xres_virtual; ++ fbinfo->yres_virtual = info->var.yres_virtual; ++ fbinfo->bpp = info->var.bits_per_pixel; ++ fbinfo->xoffset = info->var.xoffset; ++ fbinfo->yoffset = info->var.yoffset; ++ fbinfo->base = 0; // filled in by VC ++ fbinfo->pitch = 0; // filled in by VC ++ ++ printk(KERN_ERR "bcm2708_fb_set_par info(%p) %dx%d (%dx%d), %d, %d\n", info, info->var.xres, info->var.yres, info->var.xres_virtual, info->var.yres_virtual, (int)info->screen_size, info->var.bits_per_pixel ); ++ ++ // inform vc about new framebuffer ++ bcm_mailbox_write(MBOX_CHAN_FB, fb->dma); ++ ++ // wait for response ++ bcm_mailbox_read(MBOX_CHAN_FB, &val); ++ ++ fb->fb.fix.line_length = fbinfo->pitch; ++ ++ if (info->var.bits_per_pixel <= 8) ++ fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ else ++ fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; ++ ++ fb->fb.fix.smem_start = fbinfo->base; ++ fb->fb.fix.smem_len = fbinfo->pitch * fbinfo->yres_virtual; ++ fb->fb.screen_size = fbinfo->screen_size; ++ fb->fb.screen_base = (void *)ioremap_nocache(fb->fb.fix.smem_start, fb->fb.screen_size); ++ ++ printk(KERN_ERR "BCM2708FB: start = %p,%p,%p width=%d, height=%d, bpp=%d, pitch=%d\n", ++ (void *)fb->fb.screen_base, (void *)fb->fb.fix.smem_start, (void *)val, fbinfo->xres, fbinfo->yres, fbinfo->bpp, fbinfo->pitch); ++ ++ return val; ++} ++ ++static inline u32 convert_bitfield(int val, struct fb_bitfield *bf) ++{ ++ unsigned int mask = (1 << bf->length) - 1; ++ ++ return (val >> (16 - bf->length) & mask) << bf->offset; ++} ++ ++static int bcm2708_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, ++ unsigned int blue, unsigned int transp, struct fb_info *info) ++{ ++ struct bcm2708_fb *fb = to_bcm2708(info); ++ ++ if (regno < 16) ++ fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) | ++ convert_bitfield(blue, &fb->fb.var.blue) | ++ convert_bitfield(green, &fb->fb.var.green) | ++ convert_bitfield(red, &fb->fb.var.red); ++ ++ return regno > 255; ++} ++ ++static int bcm2708_fb_blank(int blank_mode, struct fb_info *info) ++{ ++//printk(KERN_ERR "bcm2708_fb_blank\n"); ++ return -1; ++} ++ ++static void bcm2708_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) ++{ ++// (is called) printk(KERN_ERR "bcm2708_fb_fillrect\n"); ++ cfb_fillrect(info, rect); ++} ++ ++static void bcm2708_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region) ++{ ++//printk(KERN_ERR "bcm2708_fb_copyarea\n"); ++ cfb_copyarea(info, region); ++} ++ ++static void bcm2708_fb_imageblit(struct fb_info *info, const struct fb_image *image) ++{ ++// (is called) printk(KERN_ERR "bcm2708_fb_imageblit\n"); ++ cfb_imageblit(info, image); ++} ++ ++static struct fb_ops bcm2708_fb_ops = { ++ .owner = THIS_MODULE, ++ .fb_check_var = bcm2708_fb_check_var, ++ .fb_set_par = bcm2708_fb_set_par, ++ .fb_setcolreg = bcm2708_fb_setcolreg, ++ .fb_blank = bcm2708_fb_blank, ++ .fb_fillrect = bcm2708_fb_fillrect, ++ .fb_copyarea = bcm2708_fb_copyarea, ++ .fb_imageblit = bcm2708_fb_imageblit, ++}; ++ ++static int FBWIDTH =800; /* module parameter */ ++static int FBHEIGHT =480; /* module parameter */ ++ ++ ++static int bcm2708_fb_register(struct bcm2708_fb *fb) ++{ ++ int ret; ++ dma_addr_t dma; ++ void *mem; ++ ++ mem = dma_alloc_coherent(NULL, PAGE_ALIGN(sizeof(*fb->info)), &dma, GFP_KERNEL); ++ ++ if (NULL == mem) { ++ printk(KERN_ERR ": unable to allocate fbinfo buffer\n"); ++ ret = -ENOMEM; ++ } else { ++ fb->info = (struct fbinfo_s *)mem; ++ fb->dma = dma; ++ } ++ fb->fb.fbops = &bcm2708_fb_ops; ++ fb->fb.flags = FBINFO_FLAG_DEFAULT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_IMAGEBLIT; ++ fb->fb.pseudo_palette = fb->cmap; ++ ++ strncpy(fb->fb.fix.id, bcm2708_name, sizeof(fb->fb.fix.id)); ++ fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; ++ fb->fb.fix.type_aux = 0; ++ fb->fb.fix.xpanstep = 0; ++ fb->fb.fix.ypanstep = 0; ++ fb->fb.fix.ywrapstep = 0; ++ fb->fb.fix.accel = FB_ACCEL_NONE; ++ ++ fb->fb.var.xres = FBWIDTH; ++ fb->fb.var.yres = FBHEIGHT; ++ fb->fb.var.xres_virtual = FBWIDTH; ++ fb->fb.var.yres_virtual = FBHEIGHT; ++ fb->fb.var.bits_per_pixel = 16; ++ fb->fb.var.vmode = FB_VMODE_NONINTERLACED; ++ fb->fb.var.activate = FB_ACTIVATE_NOW; ++ fb->fb.var.nonstd = 0; ++ fb->fb.var.height = FBWIDTH; ++ fb->fb.var.width = FBHEIGHT; ++ fb->fb.var.accel_flags = 0; ++ ++ fb->fb.monspecs.hfmin = 0; ++ fb->fb.monspecs.hfmax = 100000; ++ fb->fb.monspecs.vfmin = 0; ++ fb->fb.monspecs.vfmax = 400; ++ fb->fb.monspecs.dclkmin = 1000000; ++ fb->fb.monspecs.dclkmax = 100000000; ++ ++ bcm2708_fb_set_bitfields(&fb->fb.var); ++ ++ /* ++ * Allocate colourmap. ++ */ ++ ++ fb_set_var(&fb->fb, &fb->fb.var); ++ ++ printk(KERN_INFO "BCM2708FB: registering framebuffer (%d, %d)\n", FBWIDTH, FBHEIGHT); ++ ++ ret = register_framebuffer(&fb->fb); ++ printk(KERN_ERR "BCM2708FB: register framebuffer (%d)\n", ret); ++ if (ret == 0) ++ goto out; ++ ++ printk(KERN_ERR "BCM2708FB: cannot register framebuffer (%d)\n", ret); ++ ++ iounmap(fb->regs); ++ out: ++ return ret; ++} ++ ++static int bcm2708_fb_probe(struct platform_device *dev) ++{ ++ struct bcm2708_fb *fb; ++ int ret; ++ ++ fb = kmalloc(sizeof(struct bcm2708_fb), GFP_KERNEL); ++ if (!fb) { ++ dev_err(&dev->dev, "could not allocate new bcm2708_fb struct\n"); ++ ret = -ENOMEM; ++ goto free_region; ++ } ++ memset(fb, 0, sizeof(struct bcm2708_fb)); ++ ++ fb->dev = dev; ++ ++ ret = bcm2708_fb_register(fb); ++ if (ret == 0) { ++ platform_set_drvdata(dev, fb); ++ goto out; ++ } ++ ++ kfree(fb); ++ free_region: ++ dev_err(&dev->dev, "probe failed, err %d\n", ret); ++ out: ++ return ret; ++} ++ ++static int bcm2708_fb_remove(struct platform_device *dev) ++{ ++ struct bcm2708_fb *fb = platform_get_drvdata(dev); ++ ++ platform_set_drvdata(dev, NULL); ++ ++ unregister_framebuffer(&fb->fb); ++ iounmap(fb->regs); ++ ++ dma_free_coherent(NULL, PAGE_ALIGN(sizeof(*fb->info)), (void *)fb->info, fb->dma); ++ kfree(fb); ++ ++ return 0; ++} ++ ++static struct platform_driver bcm2708_fb_driver = { ++ .probe = bcm2708_fb_probe, ++ .remove = bcm2708_fb_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __init bcm2708_fb_init(void) ++{ ++ return platform_driver_register(&bcm2708_fb_driver); ++} ++ ++module_init(bcm2708_fb_init); ++ ++static void __exit bcm2708_fb_exit(void) ++{ ++ platform_driver_unregister(&bcm2708_fb_driver); ++} ++ ++module_exit(bcm2708_fb_exit); ++ ++module_param(FBWIDTH, int, 0644); ++module_param(FBHEIGHT, int, 0644); ++ ++MODULE_DESCRIPTION("BCM2708 framebuffer driver"); ++MODULE_LICENSE("GPL"); ++ ++MODULE_PARM_DESC(FBWIDTH, "Width of ARM Framebuffer"); ++MODULE_PARM_DESC(FBHEIGHT, "Height of ARM Framebuffer"); +diff --git a/drivers/video/logo/logo_linux_clut224.ppm b/drivers/video/logo/logo_linux_clut224.ppm +index 3c14e43..7626beb 100644 +--- a/drivers/video/logo/logo_linux_clut224.ppm ++++ b/drivers/video/logo/logo_linux_clut224.ppm +@@ -1,1604 +1,883 @@ + P3 +-# Standard 224-color Linux logo +-80 80 ++63 80 + 255 +- 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 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 0 0 0 0 0 0 +- 6 6 6 6 6 6 10 10 10 10 10 10 +- 10 10 10 6 6 6 6 6 6 6 6 6 +- 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 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 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 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 +- 0 0 0 6 6 6 10 10 10 14 14 14 +- 22 22 22 26 26 26 30 30 30 34 34 34 +- 30 30 30 30 30 30 26 26 26 18 18 18 +- 14 14 14 10 10 10 6 6 6 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 +- 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 0 0 0 0 0 0 0 0 0 +- 0 0 0 0 0 1 0 0 1 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 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 6 6 6 14 14 14 26 26 26 42 42 42 +- 54 54 54 66 66 66 78 78 78 78 78 78 +- 78 78 78 74 74 74 66 66 66 54 54 54 +- 42 42 42 26 26 26 18 18 18 10 10 10 +- 6 6 6 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 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 0 0 0 +- 0 0 1 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 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 10 10 10 +- 22 22 22 42 42 42 66 66 66 86 86 86 +- 66 66 66 38 38 38 38 38 38 22 22 22 +- 26 26 26 34 34 34 54 54 54 66 66 66 +- 86 86 86 70 70 70 46 46 46 26 26 26 +- 14 14 14 6 6 6 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 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 +- 0 0 1 0 0 1 0 0 1 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 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 0 0 0 0 0 0 10 10 10 26 26 26 +- 50 50 50 82 82 82 58 58 58 6 6 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 6 6 6 54 54 54 86 86 86 66 66 66 +- 38 38 38 18 18 18 6 6 6 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 +- 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 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 0 0 0 0 0 0 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 0 0 0 6 6 6 22 22 22 50 50 50 +- 78 78 78 34 34 34 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 6 6 6 70 70 70 +- 78 78 78 46 46 46 22 22 22 6 6 6 +- 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 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 1 0 0 1 0 0 1 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 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 6 6 6 18 18 18 42 42 42 82 82 82 +- 26 26 26 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 14 14 14 +- 46 46 46 34 34 34 6 6 6 2 2 6 +- 42 42 42 78 78 78 42 42 42 18 18 18 +- 6 6 6 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 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 1 0 0 0 0 0 1 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 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 10 10 10 30 30 30 66 66 66 58 58 58 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 26 26 26 +- 86 86 86 101 101 101 46 46 46 10 10 10 +- 2 2 6 58 58 58 70 70 70 34 34 34 +- 10 10 10 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 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 1 0 0 1 0 0 1 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 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 14 14 14 42 42 42 86 86 86 10 10 10 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 30 30 30 +- 94 94 94 94 94 94 58 58 58 26 26 26 +- 2 2 6 6 6 6 78 78 78 54 54 54 +- 22 22 22 6 6 6 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 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 +- 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 0 0 0 0 0 0 6 6 6 +- 22 22 22 62 62 62 62 62 62 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 26 26 26 +- 54 54 54 38 38 38 18 18 18 10 10 10 +- 2 2 6 2 2 6 34 34 34 82 82 82 +- 38 38 38 14 14 14 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 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 1 0 0 1 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 +- 0 0 0 0 0 0 0 0 0 6 6 6 +- 30 30 30 78 78 78 30 30 30 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 10 10 10 +- 10 10 10 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 78 78 78 +- 50 50 50 18 18 18 6 6 6 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 +- 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 1 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 0 0 0 0 0 0 +- 0 0 0 0 0 0 0 0 0 10 10 10 +- 38 38 38 86 86 86 14 14 14 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 54 54 54 +- 66 66 66 26 26 26 6 6 6 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 +- 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 1 0 0 1 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 +- 0 0 0 0 0 0 0 0 0 14 14 14 +- 42 42 42 82 82 82 2 2 6 2 2 6 +- 2 2 6 6 6 6 10 10 10 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 6 6 6 +- 14 14 14 10 10 10 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 18 18 18 +- 82 82 82 34 34 34 10 10 10 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 +- 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 1 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 0 0 0 0 0 0 +- 0 0 0 0 0 0 0 0 0 14 14 14 +- 46 46 46 86 86 86 2 2 6 2 2 6 +- 6 6 6 6 6 6 22 22 22 34 34 34 +- 6 6 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 18 18 18 34 34 34 +- 10 10 10 50 50 50 22 22 22 2 2 6 +- 2 2 6 2 2 6 2 2 6 10 10 10 +- 86 86 86 42 42 42 14 14 14 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 +- 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 1 0 0 1 0 0 1 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 +- 0 0 0 0 0 0 0 0 0 14 14 14 +- 46 46 46 86 86 86 2 2 6 2 2 6 +- 38 38 38 116 116 116 94 94 94 22 22 22 +- 22 22 22 2 2 6 2 2 6 2 2 6 +- 14 14 14 86 86 86 138 138 138 162 162 162 +-154 154 154 38 38 38 26 26 26 6 6 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 86 86 86 46 46 46 14 14 14 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 +- 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 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 0 0 0 14 14 14 +- 46 46 46 86 86 86 2 2 6 14 14 14 +-134 134 134 198 198 198 195 195 195 116 116 116 +- 10 10 10 2 2 6 2 2 6 6 6 6 +-101 98 89 187 187 187 210 210 210 218 218 218 +-214 214 214 134 134 134 14 14 14 6 6 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 86 86 86 50 50 50 18 18 18 6 6 6 +- 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 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 1 0 0 0 +- 0 0 1 0 0 1 0 0 1 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 +- 0 0 0 0 0 0 0 0 0 14 14 14 +- 46 46 46 86 86 86 2 2 6 54 54 54 +-218 218 218 195 195 195 226 226 226 246 246 246 +- 58 58 58 2 2 6 2 2 6 30 30 30 +-210 210 210 253 253 253 174 174 174 123 123 123 +-221 221 221 234 234 234 74 74 74 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 70 70 70 58 58 58 22 22 22 6 6 6 +- 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 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 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 14 14 14 +- 46 46 46 82 82 82 2 2 6 106 106 106 +-170 170 170 26 26 26 86 86 86 226 226 226 +-123 123 123 10 10 10 14 14 14 46 46 46 +-231 231 231 190 190 190 6 6 6 70 70 70 +- 90 90 90 238 238 238 158 158 158 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 70 70 70 58 58 58 22 22 22 6 6 6 +- 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 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 1 0 0 0 +- 0 0 1 0 0 1 0 0 1 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 +- 0 0 0 0 0 0 0 0 0 14 14 14 +- 42 42 42 86 86 86 6 6 6 116 116 116 +-106 106 106 6 6 6 70 70 70 149 149 149 +-128 128 128 18 18 18 38 38 38 54 54 54 +-221 221 221 106 106 106 2 2 6 14 14 14 +- 46 46 46 190 190 190 198 198 198 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 74 74 74 62 62 62 22 22 22 6 6 6 +- 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 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 1 0 0 0 +- 0 0 1 0 0 0 0 0 1 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 +- 0 0 0 0 0 0 0 0 0 14 14 14 +- 42 42 42 94 94 94 14 14 14 101 101 101 +-128 128 128 2 2 6 18 18 18 116 116 116 +-118 98 46 121 92 8 121 92 8 98 78 10 +-162 162 162 106 106 106 2 2 6 2 2 6 +- 2 2 6 195 195 195 195 195 195 6 6 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 74 74 74 62 62 62 22 22 22 6 6 6 +- 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 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 1 0 0 1 +- 0 0 1 0 0 0 0 0 1 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 +- 0 0 0 0 0 0 0 0 0 10 10 10 +- 38 38 38 90 90 90 14 14 14 58 58 58 +-210 210 210 26 26 26 54 38 6 154 114 10 +-226 170 11 236 186 11 225 175 15 184 144 12 +-215 174 15 175 146 61 37 26 9 2 2 6 +- 70 70 70 246 246 246 138 138 138 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 70 70 70 66 66 66 26 26 26 6 6 6 +- 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 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 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 10 10 10 +- 38 38 38 86 86 86 14 14 14 10 10 10 +-195 195 195 188 164 115 192 133 9 225 175 15 +-239 182 13 234 190 10 232 195 16 232 200 30 +-245 207 45 241 208 19 232 195 16 184 144 12 +-218 194 134 211 206 186 42 42 42 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 50 50 50 74 74 74 30 30 30 6 6 6 +- 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 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 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 10 10 10 +- 34 34 34 86 86 86 14 14 14 2 2 6 +-121 87 25 192 133 9 219 162 10 239 182 13 +-236 186 11 232 195 16 241 208 19 244 214 54 +-246 218 60 246 218 38 246 215 20 241 208 19 +-241 208 19 226 184 13 121 87 25 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 50 50 50 82 82 82 34 34 34 10 10 10 +- 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 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 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 10 10 10 +- 34 34 34 82 82 82 30 30 30 61 42 6 +-180 123 7 206 145 10 230 174 11 239 182 13 +-234 190 10 238 202 15 241 208 19 246 218 74 +-246 218 38 246 215 20 246 215 20 246 215 20 +-226 184 13 215 174 15 184 144 12 6 6 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 26 26 26 94 94 94 42 42 42 14 14 14 +- 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 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 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 10 10 10 +- 30 30 30 78 78 78 50 50 50 104 69 6 +-192 133 9 216 158 10 236 178 12 236 186 11 +-232 195 16 241 208 19 244 214 54 245 215 43 +-246 215 20 246 215 20 241 208 19 198 155 10 +-200 144 11 216 158 10 156 118 10 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 6 6 6 90 90 90 54 54 54 18 18 18 +- 6 6 6 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 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 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 10 10 10 +- 30 30 30 78 78 78 46 46 46 22 22 22 +-137 92 6 210 162 10 239 182 13 238 190 10 +-238 202 15 241 208 19 246 215 20 246 215 20 +-241 208 19 203 166 17 185 133 11 210 150 10 +-216 158 10 210 150 10 102 78 10 2 2 6 +- 6 6 6 54 54 54 14 14 14 2 2 6 +- 2 2 6 62 62 62 74 74 74 30 30 30 +- 10 10 10 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 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 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 10 10 10 +- 34 34 34 78 78 78 50 50 50 6 6 6 +- 94 70 30 139 102 15 190 146 13 226 184 13 +-232 200 30 232 195 16 215 174 15 190 146 13 +-168 122 10 192 133 9 210 150 10 213 154 11 +-202 150 34 182 157 106 101 98 89 2 2 6 +- 2 2 6 78 78 78 116 116 116 58 58 58 +- 2 2 6 22 22 22 90 90 90 46 46 46 +- 18 18 18 6 6 6 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 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 +- 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 10 10 10 +- 38 38 38 86 86 86 50 50 50 6 6 6 +-128 128 128 174 154 114 156 107 11 168 122 10 +-198 155 10 184 144 12 197 138 11 200 144 11 +-206 145 10 206 145 10 197 138 11 188 164 115 +-195 195 195 198 198 198 174 174 174 14 14 14 +- 2 2 6 22 22 22 116 116 116 116 116 116 +- 22 22 22 2 2 6 74 74 74 70 70 70 +- 30 30 30 10 10 10 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 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 +- 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 6 6 6 18 18 18 +- 50 50 50 101 101 101 26 26 26 10 10 10 +-138 138 138 190 190 190 174 154 114 156 107 11 +-197 138 11 200 144 11 197 138 11 192 133 9 +-180 123 7 190 142 34 190 178 144 187 187 187 +-202 202 202 221 221 221 214 214 214 66 66 66 +- 2 2 6 2 2 6 50 50 50 62 62 62 +- 6 6 6 2 2 6 10 10 10 90 90 90 +- 50 50 50 18 18 18 6 6 6 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 +- 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 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 10 10 10 34 34 34 +- 74 74 74 74 74 74 2 2 6 6 6 6 +-144 144 144 198 198 198 190 190 190 178 166 146 +-154 121 60 156 107 11 156 107 11 168 124 44 +-174 154 114 187 187 187 190 190 190 210 210 210 +-246 246 246 253 253 253 253 253 253 182 182 182 +- 6 6 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 62 62 62 +- 74 74 74 34 34 34 14 14 14 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 +- 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 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 10 10 10 22 22 22 54 54 54 +- 94 94 94 18 18 18 2 2 6 46 46 46 +-234 234 234 221 221 221 190 190 190 190 190 190 +-190 190 190 187 187 187 187 187 187 190 190 190 +-190 190 190 195 195 195 214 214 214 242 242 242 +-253 253 253 253 253 253 253 253 253 253 253 253 +- 82 82 82 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 14 14 14 +- 86 86 86 54 54 54 22 22 22 6 6 6 +- 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 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 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 +- 6 6 6 18 18 18 46 46 46 90 90 90 +- 46 46 46 18 18 18 6 6 6 182 182 182 +-253 253 253 246 246 246 206 206 206 190 190 190 +-190 190 190 190 190 190 190 190 190 190 190 190 +-206 206 206 231 231 231 250 250 250 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-202 202 202 14 14 14 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 42 42 42 86 86 86 42 42 42 18 18 18 +- 6 6 6 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 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 0 0 0 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 0 0 0 0 0 0 0 0 0 6 6 6 +- 14 14 14 38 38 38 74 74 74 66 66 66 +- 2 2 6 6 6 6 90 90 90 250 250 250 +-253 253 253 253 253 253 238 238 238 198 198 198 +-190 190 190 190 190 190 195 195 195 221 221 221 +-246 246 246 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 82 82 82 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 78 78 78 70 70 70 34 34 34 +- 14 14 14 6 6 6 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 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 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 0 0 0 0 0 0 0 0 0 14 14 14 +- 34 34 34 66 66 66 78 78 78 6 6 6 +- 2 2 6 18 18 18 218 218 218 253 253 253 +-253 253 253 253 253 253 253 253 253 246 246 246 +-226 226 226 231 231 231 246 246 246 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 178 178 178 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 18 18 18 90 90 90 62 62 62 +- 30 30 30 10 10 10 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 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 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 0 0 0 0 0 0 10 10 10 26 26 26 +- 58 58 58 90 90 90 18 18 18 2 2 6 +- 2 2 6 110 110 110 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-250 250 250 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 231 231 231 18 18 18 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 18 18 18 94 94 94 +- 54 54 54 26 26 26 10 10 10 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 +- 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 0 0 0 0 0 0 0 0 0 +- 0 0 0 6 6 6 22 22 22 50 50 50 +- 90 90 90 26 26 26 2 2 6 2 2 6 +- 14 14 14 195 195 195 250 250 250 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-250 250 250 242 242 242 54 54 54 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 38 38 38 +- 86 86 86 50 50 50 22 22 22 6 6 6 +- 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 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 0 0 0 0 0 0 +- 6 6 6 14 14 14 38 38 38 82 82 82 +- 34 34 34 2 2 6 2 2 6 2 2 6 +- 42 42 42 195 195 195 246 246 246 253 253 253 +-253 253 253 253 253 253 253 253 253 250 250 250 +-242 242 242 242 242 242 250 250 250 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 250 250 250 246 246 246 238 238 238 +-226 226 226 231 231 231 101 101 101 6 6 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 38 38 38 82 82 82 42 42 42 14 14 14 +- 6 6 6 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 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 0 0 0 +- 10 10 10 26 26 26 62 62 62 66 66 66 +- 2 2 6 2 2 6 2 2 6 6 6 6 +- 70 70 70 170 170 170 206 206 206 234 234 234 +-246 246 246 250 250 250 250 250 250 238 238 238 +-226 226 226 231 231 231 238 238 238 250 250 250 +-250 250 250 250 250 250 246 246 246 231 231 231 +-214 214 214 206 206 206 202 202 202 202 202 202 +-198 198 198 202 202 202 182 182 182 18 18 18 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 62 62 62 66 66 66 30 30 30 +- 10 10 10 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 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 0 0 0 +- 14 14 14 42 42 42 82 82 82 18 18 18 +- 2 2 6 2 2 6 2 2 6 10 10 10 +- 94 94 94 182 182 182 218 218 218 242 242 242 +-250 250 250 253 253 253 253 253 253 250 250 250 +-234 234 234 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 246 246 246 +-238 238 238 226 226 226 210 210 210 202 202 202 +-195 195 195 195 195 195 210 210 210 158 158 158 +- 6 6 6 14 14 14 50 50 50 14 14 14 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 6 6 6 86 86 86 46 46 46 +- 18 18 18 6 6 6 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 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 6 6 6 +- 22 22 22 54 54 54 70 70 70 2 2 6 +- 2 2 6 10 10 10 2 2 6 22 22 22 +-166 166 166 231 231 231 250 250 250 253 253 253 +-253 253 253 253 253 253 253 253 253 250 250 250 +-242 242 242 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 246 246 246 +-231 231 231 206 206 206 198 198 198 226 226 226 +- 94 94 94 2 2 6 6 6 6 38 38 38 +- 30 30 30 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 62 62 62 66 66 66 +- 26 26 26 10 10 10 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 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 10 10 10 +- 30 30 30 74 74 74 50 50 50 2 2 6 +- 26 26 26 26 26 26 2 2 6 106 106 106 +-238 238 238 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 246 246 246 218 218 218 202 202 202 +-210 210 210 14 14 14 2 2 6 2 2 6 +- 30 30 30 22 22 22 2 2 6 2 2 6 +- 2 2 6 2 2 6 18 18 18 86 86 86 +- 42 42 42 14 14 14 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 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 14 14 14 +- 42 42 42 90 90 90 22 22 22 2 2 6 +- 42 42 42 2 2 6 18 18 18 218 218 218 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 250 250 250 221 221 221 +-218 218 218 101 101 101 2 2 6 14 14 14 +- 18 18 18 38 38 38 10 10 10 2 2 6 +- 2 2 6 2 2 6 2 2 6 78 78 78 +- 58 58 58 22 22 22 6 6 6 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 +- 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 6 6 6 18 18 18 +- 54 54 54 82 82 82 2 2 6 26 26 26 +- 22 22 22 2 2 6 123 123 123 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 250 250 250 +-238 238 238 198 198 198 6 6 6 38 38 38 +- 58 58 58 26 26 26 38 38 38 2 2 6 +- 2 2 6 2 2 6 2 2 6 46 46 46 +- 78 78 78 30 30 30 10 10 10 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 +- 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 10 10 10 30 30 30 +- 74 74 74 58 58 58 2 2 6 42 42 42 +- 2 2 6 22 22 22 231 231 231 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 250 250 250 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 246 246 246 46 46 46 38 38 38 +- 42 42 42 14 14 14 38 38 38 14 14 14 +- 2 2 6 2 2 6 2 2 6 6 6 6 +- 86 86 86 46 46 46 14 14 14 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 +- 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 6 6 6 14 14 14 42 42 42 +- 90 90 90 18 18 18 18 18 18 26 26 26 +- 2 2 6 116 116 116 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 250 250 250 238 238 238 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 94 94 94 6 6 6 +- 2 2 6 2 2 6 10 10 10 34 34 34 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 74 74 74 58 58 58 22 22 22 6 6 6 +- 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 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 10 10 10 26 26 26 66 66 66 +- 82 82 82 2 2 6 38 38 38 6 6 6 +- 14 14 14 210 210 210 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 246 246 246 242 242 242 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 144 144 144 2 2 6 +- 2 2 6 2 2 6 2 2 6 46 46 46 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 42 42 42 74 74 74 30 30 30 10 10 10 +- 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 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 +- 6 6 6 14 14 14 42 42 42 90 90 90 +- 26 26 26 6 6 6 42 42 42 2 2 6 +- 74 74 74 250 250 250 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 242 242 242 242 242 242 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 182 182 182 2 2 6 +- 2 2 6 2 2 6 2 2 6 46 46 46 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 10 10 10 86 86 86 38 38 38 10 10 10 +- 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 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 +- 10 10 10 26 26 26 66 66 66 82 82 82 +- 2 2 6 22 22 22 18 18 18 2 2 6 +-149 149 149 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 234 234 234 242 242 242 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 206 206 206 2 2 6 +- 2 2 6 2 2 6 2 2 6 38 38 38 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 6 6 6 86 86 86 46 46 46 14 14 14 +- 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 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 6 6 6 +- 18 18 18 46 46 46 86 86 86 18 18 18 +- 2 2 6 34 34 34 10 10 10 6 6 6 +-210 210 210 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 234 234 234 242 242 242 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 221 221 221 6 6 6 +- 2 2 6 2 2 6 6 6 6 30 30 30 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 82 82 82 54 54 54 18 18 18 +- 6 6 6 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 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 10 10 10 +- 26 26 26 66 66 66 62 62 62 2 2 6 +- 2 2 6 38 38 38 10 10 10 26 26 26 +-238 238 238 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 231 231 231 238 238 238 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 231 231 231 6 6 6 +- 2 2 6 2 2 6 10 10 10 30 30 30 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 66 66 66 58 58 58 22 22 22 +- 6 6 6 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 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 10 10 10 +- 38 38 38 78 78 78 6 6 6 2 2 6 +- 2 2 6 46 46 46 14 14 14 42 42 42 +-246 246 246 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 231 231 231 242 242 242 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 234 234 234 10 10 10 +- 2 2 6 2 2 6 22 22 22 14 14 14 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 66 66 66 62 62 62 22 22 22 +- 6 6 6 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 0 0 0 0 0 0 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 0 0 0 0 0 0 6 6 6 18 18 18 +- 50 50 50 74 74 74 2 2 6 2 2 6 +- 14 14 14 70 70 70 34 34 34 62 62 62 +-250 250 250 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 231 231 231 246 246 246 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 234 234 234 14 14 14 +- 2 2 6 2 2 6 30 30 30 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 66 66 66 62 62 62 22 22 22 +- 6 6 6 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 0 0 0 0 0 0 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 0 0 0 0 0 0 6 6 6 18 18 18 +- 54 54 54 62 62 62 2 2 6 2 2 6 +- 2 2 6 30 30 30 46 46 46 70 70 70 +-250 250 250 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 231 231 231 246 246 246 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 226 226 226 10 10 10 +- 2 2 6 6 6 6 30 30 30 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 66 66 66 58 58 58 22 22 22 +- 6 6 6 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 0 0 0 0 0 0 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 0 0 0 0 0 0 6 6 6 22 22 22 +- 58 58 58 62 62 62 2 2 6 2 2 6 +- 2 2 6 2 2 6 30 30 30 78 78 78 +-250 250 250 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 231 231 231 246 246 246 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 206 206 206 2 2 6 +- 22 22 22 34 34 34 18 14 6 22 22 22 +- 26 26 26 18 18 18 6 6 6 2 2 6 +- 2 2 6 82 82 82 54 54 54 18 18 18 +- 6 6 6 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 0 0 0 0 0 0 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 0 0 0 0 0 0 6 6 6 26 26 26 +- 62 62 62 106 106 106 74 54 14 185 133 11 +-210 162 10 121 92 8 6 6 6 62 62 62 +-238 238 238 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 231 231 231 246 246 246 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 158 158 158 18 18 18 +- 14 14 14 2 2 6 2 2 6 2 2 6 +- 6 6 6 18 18 18 66 66 66 38 38 38 +- 6 6 6 94 94 94 50 50 50 18 18 18 +- 6 6 6 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 0 0 0 0 0 0 +- 0 0 0 0 0 0 0 0 0 6 6 6 +- 10 10 10 10 10 10 18 18 18 38 38 38 +- 78 78 78 142 134 106 216 158 10 242 186 14 +-246 190 14 246 190 14 156 118 10 10 10 10 +- 90 90 90 238 238 238 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 231 231 231 250 250 250 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 246 230 190 +-238 204 91 238 204 91 181 142 44 37 26 9 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 38 38 38 46 46 46 +- 26 26 26 106 106 106 54 54 54 18 18 18 +- 6 6 6 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 0 0 0 0 0 0 +- 0 0 0 6 6 6 14 14 14 22 22 22 +- 30 30 30 38 38 38 50 50 50 70 70 70 +-106 106 106 190 142 34 226 170 11 242 186 14 +-246 190 14 246 190 14 246 190 14 154 114 10 +- 6 6 6 74 74 74 226 226 226 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 231 231 231 250 250 250 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 228 184 62 +-241 196 14 241 208 19 232 195 16 38 30 10 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 6 6 6 30 30 30 26 26 26 +-203 166 17 154 142 90 66 66 66 26 26 26 +- 6 6 6 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 0 0 0 0 0 0 +- 6 6 6 18 18 18 38 38 38 58 58 58 +- 78 78 78 86 86 86 101 101 101 123 123 123 +-175 146 61 210 150 10 234 174 13 246 186 14 +-246 190 14 246 190 14 246 190 14 238 190 10 +-102 78 10 2 2 6 46 46 46 198 198 198 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 234 234 234 242 242 242 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 224 178 62 +-242 186 14 241 196 14 210 166 10 22 18 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 6 6 6 121 92 8 +-238 202 15 232 195 16 82 82 82 34 34 34 +- 10 10 10 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 0 0 0 0 0 0 +- 14 14 14 38 38 38 70 70 70 154 122 46 +-190 142 34 200 144 11 197 138 11 197 138 11 +-213 154 11 226 170 11 242 186 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-225 175 15 46 32 6 2 2 6 22 22 22 +-158 158 158 250 250 250 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 250 250 250 242 242 242 224 178 62 +-239 182 13 236 186 11 213 154 11 46 32 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 61 42 6 225 175 15 +-238 190 10 236 186 11 112 100 78 42 42 42 +- 14 14 14 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 0 0 0 6 6 6 +- 22 22 22 54 54 54 154 122 46 213 154 11 +-226 170 11 230 174 11 226 170 11 226 170 11 +-236 178 12 242 186 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-241 196 14 184 144 12 10 10 10 2 2 6 +- 6 6 6 116 116 116 242 242 242 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 231 231 231 198 198 198 214 170 54 +-236 178 12 236 178 12 210 150 10 137 92 6 +- 18 14 6 2 2 6 2 2 6 2 2 6 +- 6 6 6 70 47 6 200 144 11 236 178 12 +-239 182 13 239 182 13 124 112 88 58 58 58 +- 22 22 22 6 6 6 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 10 10 10 +- 30 30 30 70 70 70 180 133 36 226 170 11 +-239 182 13 242 186 14 242 186 14 246 186 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 232 195 16 98 70 6 2 2 6 +- 2 2 6 2 2 6 66 66 66 221 221 221 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 206 206 206 198 198 198 214 166 58 +-230 174 11 230 174 11 216 158 10 192 133 9 +-163 110 8 116 81 8 102 78 10 116 81 8 +-167 114 7 197 138 11 226 170 11 239 182 13 +-242 186 14 242 186 14 162 146 94 78 78 78 +- 34 34 34 14 14 14 6 6 6 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 6 6 6 +- 30 30 30 78 78 78 190 142 34 226 170 11 +-239 182 13 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 241 196 14 203 166 17 22 18 6 +- 2 2 6 2 2 6 2 2 6 38 38 38 +-218 218 218 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-250 250 250 206 206 206 198 198 198 202 162 69 +-226 170 11 236 178 12 224 166 10 210 150 10 +-200 144 11 197 138 11 192 133 9 197 138 11 +-210 150 10 226 170 11 242 186 14 246 190 14 +-246 190 14 246 186 14 225 175 15 124 112 88 +- 62 62 62 30 30 30 14 14 14 6 6 6 +- 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 10 10 10 +- 30 30 30 78 78 78 174 135 50 224 166 10 +-239 182 13 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 241 196 14 139 102 15 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 78 78 78 250 250 250 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-250 250 250 214 214 214 198 198 198 190 150 46 +-219 162 10 236 178 12 234 174 13 224 166 10 +-216 158 10 213 154 11 213 154 11 216 158 10 +-226 170 11 239 182 13 246 190 14 246 190 14 +-246 190 14 246 190 14 242 186 14 206 162 42 +-101 101 101 58 58 58 30 30 30 14 14 14 +- 6 6 6 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 10 10 10 +- 30 30 30 74 74 74 174 135 50 216 158 10 +-236 178 12 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 241 196 14 226 184 13 +- 61 42 6 2 2 6 2 2 6 2 2 6 +- 22 22 22 238 238 238 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 226 226 226 187 187 187 180 133 36 +-216 158 10 236 178 12 239 182 13 236 178 12 +-230 174 11 226 170 11 226 170 11 230 174 11 +-236 178 12 242 186 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 186 14 239 182 13 +-206 162 42 106 106 106 66 66 66 34 34 34 +- 14 14 14 6 6 6 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 6 6 6 +- 26 26 26 70 70 70 163 133 67 213 154 11 +-236 178 12 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 241 196 14 +-190 146 13 18 14 6 2 2 6 2 2 6 +- 46 46 46 246 246 246 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 221 221 221 86 86 86 156 107 11 +-216 158 10 236 178 12 242 186 14 246 186 14 +-242 186 14 239 182 13 239 182 13 242 186 14 +-242 186 14 246 186 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-242 186 14 225 175 15 142 122 72 66 66 66 +- 30 30 30 10 10 10 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 6 6 6 +- 26 26 26 70 70 70 163 133 67 210 150 10 +-236 178 12 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-232 195 16 121 92 8 34 34 34 106 106 106 +-221 221 221 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-242 242 242 82 82 82 18 14 6 163 110 8 +-216 158 10 236 178 12 242 186 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 242 186 14 163 133 67 +- 46 46 46 18 18 18 6 6 6 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 10 10 10 +- 30 30 30 78 78 78 163 133 67 210 150 10 +-236 178 12 246 186 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-241 196 14 215 174 15 190 178 144 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 218 218 218 +- 58 58 58 2 2 6 22 18 6 167 114 7 +-216 158 10 236 178 12 246 186 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 186 14 242 186 14 190 150 46 +- 54 54 54 22 22 22 6 6 6 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 14 14 14 +- 38 38 38 86 86 86 180 133 36 213 154 11 +-236 178 12 246 186 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 232 195 16 190 146 13 214 214 214 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 250 250 250 170 170 170 26 26 26 +- 2 2 6 2 2 6 37 26 9 163 110 8 +-219 162 10 239 182 13 246 186 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 186 14 236 178 12 224 166 10 142 122 72 +- 46 46 46 18 18 18 6 6 6 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 6 6 6 18 18 18 +- 50 50 50 109 106 95 192 133 9 224 166 10 +-242 186 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-242 186 14 226 184 13 210 162 10 142 110 46 +-226 226 226 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-253 253 253 253 253 253 253 253 253 253 253 253 +-198 198 198 66 66 66 2 2 6 2 2 6 +- 2 2 6 2 2 6 50 34 6 156 107 11 +-219 162 10 239 182 13 246 186 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 242 186 14 +-234 174 13 213 154 11 154 122 46 66 66 66 +- 30 30 30 10 10 10 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 6 6 6 22 22 22 +- 58 58 58 154 121 60 206 145 10 234 174 13 +-242 186 14 246 186 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 186 14 236 178 12 210 162 10 163 110 8 +- 61 42 6 138 138 138 218 218 218 250 250 250 +-253 253 253 253 253 253 253 253 253 250 250 250 +-242 242 242 210 210 210 144 144 144 66 66 66 +- 6 6 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 61 42 6 163 110 8 +-216 158 10 236 178 12 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 239 182 13 230 174 11 216 158 10 +-190 142 34 124 112 88 70 70 70 38 38 38 +- 18 18 18 6 6 6 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 6 6 6 22 22 22 +- 62 62 62 168 124 44 206 145 10 224 166 10 +-236 178 12 239 182 13 242 186 14 242 186 14 +-246 186 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 236 178 12 216 158 10 175 118 6 +- 80 54 7 2 2 6 6 6 6 30 30 30 +- 54 54 54 62 62 62 50 50 50 38 38 38 +- 14 14 14 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 6 6 6 80 54 7 167 114 7 +-213 154 11 236 178 12 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 190 14 242 186 14 239 182 13 239 182 13 +-230 174 11 210 150 10 174 135 50 124 112 88 +- 82 82 82 54 54 54 34 34 34 18 18 18 +- 6 6 6 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 6 6 6 18 18 18 +- 50 50 50 158 118 36 192 133 9 200 144 11 +-216 158 10 219 162 10 224 166 10 226 170 11 +-230 174 11 236 178 12 239 182 13 239 182 13 +-242 186 14 246 186 14 246 190 14 246 190 14 +-246 190 14 246 190 14 246 190 14 246 190 14 +-246 186 14 230 174 11 210 150 10 163 110 8 +-104 69 6 10 10 10 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 6 6 6 91 60 6 167 114 7 +-206 145 10 230 174 11 242 186 14 246 190 14 +-246 190 14 246 190 14 246 186 14 242 186 14 +-239 182 13 230 174 11 224 166 10 213 154 11 +-180 133 36 124 112 88 86 86 86 58 58 58 +- 38 38 38 22 22 22 10 10 10 6 6 6 +- 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 14 14 14 +- 34 34 34 70 70 70 138 110 50 158 118 36 +-167 114 7 180 123 7 192 133 9 197 138 11 +-200 144 11 206 145 10 213 154 11 219 162 10 +-224 166 10 230 174 11 239 182 13 242 186 14 +-246 186 14 246 186 14 246 186 14 246 186 14 +-239 182 13 216 158 10 185 133 11 152 99 6 +-104 69 6 18 14 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 2 2 6 2 2 6 2 2 6 +- 2 2 6 6 6 6 80 54 7 152 99 6 +-192 133 9 219 162 10 236 178 12 239 182 13 +-246 186 14 242 186 14 239 182 13 236 178 12 +-224 166 10 206 145 10 192 133 9 154 121 60 +- 94 94 94 62 62 62 42 42 42 22 22 22 +- 14 14 14 6 6 6 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 6 6 6 +- 18 18 18 34 34 34 58 58 58 78 78 78 +-101 98 89 124 112 88 142 110 46 156 107 11 +-163 110 8 167 114 7 175 118 6 180 123 7 +-185 133 11 197 138 11 210 150 10 219 162 10 +-226 170 11 236 178 12 236 178 12 234 174 13 +-219 162 10 197 138 11 163 110 8 130 83 6 +- 91 60 6 10 10 10 2 2 6 2 2 6 +- 18 18 18 38 38 38 38 38 38 38 38 38 +- 38 38 38 38 38 38 38 38 38 38 38 38 +- 38 38 38 38 38 38 26 26 26 2 2 6 +- 2 2 6 6 6 6 70 47 6 137 92 6 +-175 118 6 200 144 11 219 162 10 230 174 11 +-234 174 13 230 174 11 219 162 10 210 150 10 +-192 133 9 163 110 8 124 112 88 82 82 82 +- 50 50 50 30 30 30 14 14 14 6 6 6 +- 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 0 0 0 0 0 0 0 0 0 +- 6 6 6 14 14 14 22 22 22 34 34 34 +- 42 42 42 58 58 58 74 74 74 86 86 86 +-101 98 89 122 102 70 130 98 46 121 87 25 +-137 92 6 152 99 6 163 110 8 180 123 7 +-185 133 11 197 138 11 206 145 10 200 144 11 +-180 123 7 156 107 11 130 83 6 104 69 6 +- 50 34 6 54 54 54 110 110 110 101 98 89 +- 86 86 86 82 82 82 78 78 78 78 78 78 +- 78 78 78 78 78 78 78 78 78 78 78 78 +- 78 78 78 82 82 82 86 86 86 94 94 94 +-106 106 106 101 101 101 86 66 34 124 80 6 +-156 107 11 180 123 7 192 133 9 200 144 11 +-206 145 10 200 144 11 192 133 9 175 118 6 +-139 102 15 109 106 95 70 70 70 42 42 42 +- 22 22 22 10 10 10 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 0 0 0 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 0 0 0 0 0 0 6 6 6 10 10 10 +- 14 14 14 22 22 22 30 30 30 38 38 38 +- 50 50 50 62 62 62 74 74 74 90 90 90 +-101 98 89 112 100 78 121 87 25 124 80 6 +-137 92 6 152 99 6 152 99 6 152 99 6 +-138 86 6 124 80 6 98 70 6 86 66 30 +-101 98 89 82 82 82 58 58 58 46 46 46 +- 38 38 38 34 34 34 34 34 34 34 34 34 +- 34 34 34 34 34 34 34 34 34 34 34 34 +- 34 34 34 34 34 34 38 38 38 42 42 42 +- 54 54 54 82 82 82 94 86 76 91 60 6 +-134 86 6 156 107 11 167 114 7 175 118 6 +-175 118 6 167 114 7 152 99 6 121 87 25 +-101 98 89 62 62 62 34 34 34 18 18 18 +- 6 6 6 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 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 6 6 6 6 6 6 10 10 10 +- 18 18 18 22 22 22 30 30 30 42 42 42 +- 50 50 50 66 66 66 86 86 86 101 98 89 +-106 86 58 98 70 6 104 69 6 104 69 6 +-104 69 6 91 60 6 82 62 34 90 90 90 +- 62 62 62 38 38 38 22 22 22 14 14 14 +- 10 10 10 10 10 10 10 10 10 10 10 10 +- 10 10 10 10 10 10 6 6 6 10 10 10 +- 10 10 10 10 10 10 10 10 10 14 14 14 +- 22 22 22 42 42 42 70 70 70 89 81 66 +- 80 54 7 104 69 6 124 80 6 137 92 6 +-134 86 6 116 81 8 100 82 52 86 86 86 +- 58 58 58 30 30 30 14 14 14 6 6 6 +- 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 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 6 6 6 10 10 10 14 14 14 +- 18 18 18 26 26 26 38 38 38 54 54 54 +- 70 70 70 86 86 86 94 86 76 89 81 66 +- 89 81 66 86 86 86 74 74 74 50 50 50 +- 30 30 30 14 14 14 6 6 6 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 +- 6 6 6 18 18 18 34 34 34 58 58 58 +- 82 82 82 89 81 66 89 81 66 89 81 66 +- 94 86 66 94 86 76 74 74 74 50 50 50 +- 26 26 26 14 14 14 6 6 6 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 +- 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 0 0 0 0 0 0 0 0 0 +- 6 6 6 6 6 6 14 14 14 18 18 18 +- 30 30 30 38 38 38 46 46 46 54 54 54 +- 50 50 50 42 42 42 30 30 30 18 18 18 +- 10 10 10 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 6 6 6 14 14 14 26 26 26 +- 38 38 38 50 50 50 58 58 58 58 58 58 +- 54 54 54 42 42 42 30 30 30 18 18 18 +- 10 10 10 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 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 0 0 0 +- 0 0 0 0 0 0 0 0 0 0 0 0 +- 0 0 0 0 0 0 0 0 0 6 6 6 +- 6 6 6 10 10 10 14 14 14 18 18 18 +- 18 18 18 14 14 14 10 10 10 6 6 6 +- 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 0 0 0 0 0 0 6 6 6 +- 14 14 14 18 18 18 22 22 22 22 22 22 +- 18 18 18 14 14 14 10 10 10 6 6 6 +- 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 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 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 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 ++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 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 1 0 0 0 0 0 0 0 1 1 0 ++0 1 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 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 1 1 0 0 0 0 0 0 0 ++0 1 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 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 1 1 0 ++10 15 3 2 3 1 12 18 4 42 61 14 19 27 6 11 16 4 ++38 55 13 10 15 3 3 4 1 10 15 3 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 0 0 0 0 0 0 2 3 1 ++12 18 4 1 1 0 23 34 8 31 45 11 10 15 3 32 47 11 ++34 49 12 3 4 1 3 4 1 3 4 1 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 ++0 0 0 0 0 0 10 15 3 29 42 10 26 37 9 12 18 4 ++55 80 19 81 118 28 55 80 19 92 132 31 106 153 36 69 100 23 ++100 144 34 80 116 27 42 61 14 81 118 28 23 34 8 27 40 9 ++15 21 5 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 1 1 0 29 42 10 15 21 5 50 72 17 ++74 107 25 45 64 15 102 148 35 80 116 27 84 121 28 111 160 38 ++69 100 23 65 94 22 81 118 28 29 42 10 17 25 6 29 42 10 ++23 34 8 2 3 1 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 3 4 1 ++15 21 5 15 21 5 34 49 12 101 146 34 111 161 38 97 141 33 ++97 141 33 119 172 41 117 170 40 116 167 40 118 170 40 118 171 40 ++117 169 40 118 170 40 111 160 38 118 170 40 96 138 32 89 128 30 ++81 118 28 11 16 4 10 15 3 1 1 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 ++3 4 1 3 4 1 34 49 12 101 146 34 79 115 27 111 160 38 ++114 165 39 113 163 39 118 170 40 117 169 40 118 171 40 117 169 40 ++116 167 40 119 172 41 113 163 39 92 132 31 105 151 36 113 163 39 ++75 109 26 19 27 6 16 23 5 11 16 4 0 1 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 10 15 3 ++80 116 27 106 153 36 105 151 36 114 165 39 118 170 40 118 171 40 ++118 171 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 170 40 117 169 40 118 170 40 118 170 40 ++117 170 40 75 109 26 75 109 26 34 49 12 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 4 1 ++64 92 22 65 94 22 100 144 34 118 171 40 118 170 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 118 171 41 118 170 40 117 169 40 ++109 158 37 105 151 36 104 150 35 47 69 16 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 ++42 61 14 115 167 39 118 170 40 117 169 40 117 169 40 117 169 40 ++117 170 40 117 170 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 118 170 40 96 138 32 17 25 6 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 47 69 16 ++114 165 39 117 168 40 117 170 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 118 170 40 117 169 40 117 169 40 117 169 40 ++117 170 40 119 172 41 96 138 32 12 18 4 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 10 15 3 ++32 47 11 105 151 36 118 170 40 117 169 40 117 169 40 116 168 40 ++109 157 37 111 160 38 117 169 40 118 171 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 118 171 40 69 100 23 2 3 1 ++0 0 0 0 0 0 0 0 0 0 0 0 19 27 6 101 146 34 ++118 171 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 170 40 ++118 171 40 115 166 39 107 154 36 111 161 38 117 169 40 117 169 40 ++117 169 40 118 171 40 75 109 26 19 27 6 2 3 1 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 16 23 5 ++89 128 30 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++111 160 38 92 132 31 79 115 27 96 138 32 115 166 39 119 171 41 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 118 170 40 109 157 37 26 37 9 ++0 0 0 0 0 0 0 0 0 0 0 0 64 92 22 118 171 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 118 170 40 118 171 40 109 157 37 ++89 128 30 81 118 28 100 144 34 115 166 39 117 169 40 117 169 40 ++117 169 40 117 170 40 113 163 39 60 86 20 1 1 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 ++27 40 9 96 138 32 118 170 40 117 169 40 117 169 40 117 169 40 ++117 170 40 117 169 40 101 146 34 67 96 23 55 80 19 84 121 28 ++113 163 39 119 171 41 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 119 171 41 65 94 22 ++0 0 0 0 0 0 0 0 0 15 21 5 101 146 34 118 171 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 118 170 40 118 171 40 104 150 35 69 100 23 53 76 18 ++81 118 28 111 160 38 118 170 40 117 169 40 117 169 40 117 169 40 ++117 169 40 114 165 39 69 100 23 10 15 3 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 1 1 0 ++31 45 11 77 111 26 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 118 170 40 116 168 40 92 132 31 47 69 16 ++38 55 13 81 118 28 113 163 39 119 171 41 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 118 171 41 92 132 31 ++10 15 3 0 0 0 0 0 0 36 52 12 115 166 39 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 118 170 40 ++118 171 40 102 148 35 64 92 22 34 49 12 65 94 22 106 153 36 ++118 171 40 117 170 40 117 169 40 117 169 40 117 169 40 117 169 40 ++118 170 40 107 154 36 55 80 19 15 21 5 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 ++29 42 10 101 146 34 118 171 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 118 171 40 113 163 39 ++75 109 26 27 40 9 36 52 12 89 128 30 116 167 40 118 171 40 ++117 169 40 117 169 40 117 169 40 117 169 40 118 170 40 104 150 35 ++16 23 5 0 0 0 0 0 0 53 76 18 118 171 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 119 171 41 109 157 37 ++67 96 23 23 34 8 42 61 14 96 138 32 118 170 40 118 170 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 74 107 25 10 15 3 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 31 45 11 101 146 34 118 170 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++119 171 41 102 148 35 47 69 16 14 20 5 50 72 17 102 148 35 ++118 171 40 117 169 40 117 169 40 117 169 40 118 170 40 102 148 35 ++15 21 5 0 0 0 0 0 0 50 72 17 118 170 40 117 169 40 ++117 169 40 117 169 40 118 170 40 116 167 40 84 121 28 27 40 9 ++19 27 6 74 107 25 114 165 39 118 171 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 75 109 26 10 15 4 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 38 55 13 102 148 35 118 171 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 118 170 40 115 167 39 77 111 26 17 25 6 19 27 6 ++77 111 26 115 166 39 118 170 40 117 169 40 119 172 41 81 118 28 ++3 4 1 0 0 0 0 0 0 27 40 9 111 160 38 118 170 40 ++117 169 40 118 171 40 105 151 36 50 72 17 10 15 3 38 55 13 ++100 144 34 118 171 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 79 115 27 15 21 5 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 10 15 3 64 92 22 111 160 38 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 118 171 40 96 138 32 32 47 11 ++3 4 1 50 72 17 107 154 36 120 173 41 105 151 36 31 45 11 ++0 0 0 0 0 0 0 0 0 3 4 1 65 94 22 117 169 40 ++118 170 40 89 128 30 26 37 9 3 4 1 60 86 20 111 161 38 ++118 171 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++97 141 33 36 52 12 1 1 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 14 20 5 75 109 26 117 168 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 118 171 40 107 154 36 ++45 64 15 2 3 1 31 45 11 75 109 26 32 47 11 0 1 0 ++0 0 0 0 0 0 0 0 0 0 0 0 10 15 3 55 80 19 ++65 94 22 11 16 4 11 16 4 75 109 26 116 168 40 118 170 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 118 170 40 107 154 36 ++47 69 16 3 4 1 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 12 18 4 69 100 23 111 161 38 118 171 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 118 170 40 ++111 160 38 50 72 17 2 3 1 2 3 1 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 0 ++1 1 0 12 18 4 81 118 28 118 170 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 170 40 118 171 40 101 146 34 ++42 61 14 2 3 1 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 3 4 1 36 52 12 89 128 30 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++118 171 41 101 146 34 14 20 5 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 47 69 16 118 170 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 170 40 111 160 38 69 100 23 19 27 6 ++0 1 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 0 0 0 0 11 16 4 69 100 23 ++115 167 39 119 172 41 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++119 172 41 75 109 26 3 4 1 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 23 34 8 106 153 36 118 170 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++117 169 40 118 170 40 119 172 41 105 151 36 42 61 14 2 3 1 ++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 0 0 0 0 0 0 1 1 0 15 21 5 ++45 64 15 80 116 27 114 165 39 118 170 40 117 169 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 119 172 41 ++97 141 33 20 30 7 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 1 1 0 53 76 18 114 165 39 118 171 40 117 169 40 ++117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 117 169 40 ++118 171 40 104 150 35 64 92 22 31 45 11 10 15 3 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 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++0 0 0 36 52 12 97 141 33 109 158 37 113 163 39 116 168 40 ++117 169 40 117 170 40 118 170 40 119 172 41 115 167 39 84 121 28 ++23 34 8 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 3 4 1 50 72 17 102 148 35 118 171 40 ++119 171 41 118 170 40 117 169 40 117 169 40 115 166 39 111 161 38 ++109 157 37 79 115 27 12 18 4 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 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++0 0 0 3 4 1 15 21 5 23 34 8 45 64 15 106 153 36 ++116 167 40 111 160 38 101 146 34 79 115 27 42 61 14 10 15 3 ++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 1 1 0 20 30 7 60 86 20 ++89 128 30 106 153 36 113 163 39 117 169 40 84 121 28 29 42 10 ++19 27 6 10 15 3 2 3 1 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 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 16 23 5 38 55 13 ++36 52 12 26 37 9 12 18 4 2 3 1 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 1 0 0 19 2 7 52 5 18 ++78 7 27 88 8 31 81 7 29 56 5 19 25 2 9 3 0 1 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++3 4 1 19 27 6 31 45 11 38 55 13 32 47 11 3 4 1 ++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 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 3 0 1 ++9 0 3 12 1 4 9 0 3 4 0 1 0 0 0 0 0 0 ++0 0 0 0 0 0 28 3 10 99 9 35 156 14 55 182 16 64 ++189 17 66 190 17 67 189 17 66 184 17 65 166 15 58 118 13 41 ++45 4 16 3 0 1 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 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 0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 11 1 4 52 5 18 101 9 35 134 12 47 ++151 14 53 154 14 54 151 14 53 113 10 40 11 1 4 0 0 0 ++3 0 1 67 6 24 159 14 56 190 17 67 190 17 67 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 190 17 67 191 17 67 ++174 16 61 101 9 35 14 1 5 0 0 0 35 3 12 108 10 38 ++122 11 43 122 11 43 112 10 39 87 8 30 50 5 17 13 1 5 ++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 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 ++3 0 1 56 5 19 141 13 49 182 16 64 191 17 67 191 17 67 ++190 17 67 190 17 67 191 17 67 113 10 40 3 0 1 1 0 0 ++79 7 28 180 16 63 190 17 67 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++189 17 66 188 17 66 122 11 43 11 1 4 41 4 14 176 16 62 ++191 17 67 191 17 67 191 17 67 190 17 67 181 16 63 146 13 51 ++75 7 26 10 1 4 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 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 7 1 2 ++90 8 32 178 16 62 191 17 67 188 17 66 188 17 66 188 17 66 ++188 17 66 190 17 67 141 13 49 22 2 8 0 0 0 41 4 14 ++173 16 61 190 17 67 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 88 8 31 1 0 0 89 8 31 ++185 17 65 189 17 66 188 17 66 188 17 66 189 17 66 191 17 67 ++186 17 65 124 11 43 25 2 9 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 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 2 0 1 89 8 31 ++184 17 65 189 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++190 17 67 151 14 53 34 3 12 0 0 0 0 0 0 79 7 28 ++190 17 67 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 191 17 67 146 13 51 9 1 3 7 1 2 ++108 10 38 187 17 66 189 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 190 17 67 141 13 49 22 2 8 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 ++0 0 0 0 0 0 0 0 0 0 0 0 52 5 18 176 16 62 ++189 17 66 188 17 66 188 17 66 188 17 66 188 17 66 190 17 67 ++151 14 53 38 3 13 0 0 0 0 0 0 0 0 0 50 5 17 ++180 16 63 189 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 191 17 67 141 13 49 7 1 3 0 0 0 ++11 1 4 112 10 39 187 17 66 189 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 190 17 67 113 10 40 5 0 2 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 0 0 0 0 0 0 7 1 3 132 12 46 191 17 67 ++188 17 66 188 17 66 188 17 66 188 17 66 190 17 67 146 13 51 ++35 3 12 0 0 0 0 0 0 0 0 0 0 0 0 5 0 2 ++101 9 35 185 17 65 190 17 67 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 190 17 67 180 16 63 67 6 24 0 0 0 0 0 0 ++0 0 0 11 1 4 108 10 38 186 17 65 189 17 66 188 17 66 ++188 17 66 188 17 66 189 17 66 180 16 63 56 5 19 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 0 0 0 0 0 0 44 4 15 177 16 62 189 17 66 ++188 17 66 188 17 66 189 17 66 189 17 66 134 12 47 28 3 10 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++8 1 3 79 7 28 159 14 56 188 17 66 191 17 67 190 17 67 ++189 17 66 189 17 66 189 17 66 189 17 66 190 17 67 191 17 67 ++188 17 66 158 14 55 72 7 25 4 0 1 0 0 0 0 0 0 ++0 0 0 0 0 0 8 1 3 95 9 33 182 16 64 189 17 67 ++188 17 66 188 17 66 188 17 66 191 17 67 122 11 43 3 0 1 ++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 0 0 0 88 8 31 190 17 67 188 17 66 ++188 17 66 189 17 66 185 17 65 113 10 40 18 2 6 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++0 0 0 1 0 0 24 2 8 77 7 27 124 11 43 154 14 54 ++168 15 59 173 16 61 173 16 61 168 15 59 154 14 54 124 11 43 ++77 7 27 22 2 8 0 0 0 0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 5 0 2 77 7 27 173 16 61 ++190 17 67 188 17 66 188 17 66 190 17 67 164 15 57 23 2 8 ++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 1 0 0 118 13 41 191 17 67 188 17 66 ++190 17 67 174 16 61 87 8 30 8 1 3 0 0 0 0 0 0 ++0 0 0 0 0 0 10 1 4 29 3 10 40 4 14 36 3 13 ++18 2 6 2 0 1 0 0 0 0 0 0 3 0 1 14 1 5 ++26 2 9 33 3 11 32 3 11 25 2 9 13 1 5 3 0 1 ++0 0 0 14 1 5 56 5 19 95 9 33 109 10 38 101 9 35 ++77 7 27 35 3 12 5 0 2 0 0 0 1 0 0 56 5 19 ++156 14 55 190 17 67 188 17 66 188 17 66 182 16 64 50 5 17 ++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 5 0 2 134 12 47 191 17 67 189 17 66 ++151 14 53 52 5 18 2 0 1 0 0 0 0 0 0 1 0 0 ++28 3 10 90 8 32 146 13 51 170 15 60 178 16 62 174 16 61 ++158 14 55 112 10 39 40 4 14 1 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 ++56 5 19 146 13 51 183 17 64 191 17 67 191 17 67 191 17 67 ++188 17 66 173 16 61 122 11 43 41 4 14 1 0 0 0 0 0 ++30 3 10 124 11 43 185 17 65 190 17 67 187 17 66 67 6 24 ++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 6 1 2 134 12 47 168 15 59 99 9 35 ++21 2 7 0 0 0 0 0 0 0 0 0 6 1 2 77 7 27 ++162 15 57 190 17 67 191 17 67 189 17 66 189 17 66 189 17 66 ++190 17 67 191 17 67 169 15 59 75 7 26 3 0 1 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 2 0 1 79 7 28 ++178 16 62 191 17 67 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 189 17 66 191 17 67 170 15 60 79 7 28 5 0 2 ++0 0 0 10 1 3 78 7 27 159 14 56 188 17 66 75 7 26 ++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 1 0 0 35 3 12 29 3 10 2 0 1 ++0 0 0 0 0 0 0 0 0 9 1 3 101 9 35 183 17 64 ++190 17 67 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 190 17 67 178 16 63 67 6 23 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 52 5 18 174 16 61 ++190 17 67 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 190 17 67 182 16 64 89 8 31 ++4 0 1 0 0 0 0 0 0 25 2 9 73 7 26 31 3 11 ++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 0 0 0 0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 4 0 1 98 9 34 187 17 66 189 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 190 17 67 158 14 55 25 2 9 ++0 0 0 0 0 0 0 0 0 8 1 3 134 12 47 191 17 67 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 189 17 66 180 16 63 ++68 6 24 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 0 0 0 0 0 0 0 0 0 ++0 0 0 6 1 2 19 2 7 3 0 1 0 0 0 0 0 0 ++0 0 0 0 0 0 65 6 23 180 16 63 189 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 189 17 66 83 8 29 ++0 0 0 0 0 0 0 0 0 41 4 14 177 16 62 189 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 190 17 67 ++159 14 56 28 3 10 0 0 0 0 0 0 0 0 0 23 2 8 ++41 4 14 5 0 2 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 ++23 2 8 113 10 40 159 14 56 65 6 23 0 0 0 0 0 0 ++0 0 0 16 1 6 146 13 51 191 17 67 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 191 17 67 132 12 46 ++5 0 2 0 0 0 0 0 0 77 7 27 189 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++190 17 67 98 9 34 0 0 0 0 0 0 12 1 4 134 12 47 ++178 16 63 108 10 38 16 1 6 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 30 3 10 ++141 13 49 190 17 67 191 17 67 134 12 47 6 1 2 0 0 0 ++0 0 0 68 6 24 186 17 65 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 190 17 67 156 14 55 ++14 1 5 0 0 0 0 0 0 98 9 34 191 17 67 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++190 17 67 156 14 55 19 2 7 0 0 0 47 4 16 181 16 63 ++190 17 67 189 17 66 126 14 44 17 2 6 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 16 1 6 134 12 47 ++191 17 67 188 17 66 190 17 67 162 15 57 19 2 7 0 0 0 ++3 0 1 123 11 43 191 17 67 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 190 17 67 163 15 57 ++20 2 7 0 0 0 0 0 0 101 9 35 191 17 67 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 182 16 64 52 5 18 0 0 0 73 7 26 188 17 66 ++188 17 66 188 17 66 189 17 66 109 10 38 5 0 2 0 0 0 ++0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 95 9 33 189 17 66 ++188 17 66 188 17 66 189 17 66 171 15 60 29 3 10 0 0 0 ++16 1 6 156 14 55 190 17 67 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 190 17 67 158 14 55 ++17 2 6 0 0 0 0 0 0 85 8 30 190 17 67 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 189 17 66 81 7 29 0 0 0 85 8 30 190 17 67 ++188 17 66 188 17 66 189 17 66 180 16 63 56 5 19 0 0 0 ++0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 25 2 9 162 15 57 190 17 67 ++188 17 66 188 17 66 189 17 66 173 16 61 31 3 11 0 0 0 ++30 3 10 171 15 60 189 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 191 17 67 141 13 49 ++7 1 2 0 0 0 0 0 0 56 5 19 183 17 64 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 191 17 67 98 9 34 0 0 0 88 8 31 190 17 67 ++188 17 66 188 17 66 188 17 66 191 17 67 124 11 43 5 0 2 ++0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 68 6 24 187 17 66 188 17 66 ++188 17 66 188 17 66 189 17 66 170 15 60 28 3 10 0 0 0 ++34 3 12 174 16 61 189 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 191 17 67 101 9 35 ++0 0 0 0 0 0 0 0 0 21 2 7 159 14 56 190 17 67 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 191 17 67 98 9 34 0 0 0 81 7 29 189 17 66 ++188 17 66 188 17 66 188 17 66 189 17 66 168 15 59 28 3 10 ++0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 109 10 38 191 17 67 188 17 66 ++188 17 66 188 17 66 190 17 67 163 15 57 21 2 7 0 0 0 ++26 2 9 168 15 59 189 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 189 17 66 180 16 63 47 4 16 ++0 0 0 0 0 0 0 0 0 0 0 0 108 10 38 190 17 67 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 189 17 66 78 7 27 0 0 0 68 6 24 187 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 183 17 64 56 5 19 ++0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 3 0 1 131 12 46 191 17 67 188 17 66 ++188 17 66 188 17 66 190 17 67 151 14 53 12 1 4 0 0 0 ++11 1 4 146 13 51 190 17 67 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 191 17 67 126 14 44 7 1 2 ++0 0 0 0 0 0 0 0 0 0 0 0 32 3 11 164 15 58 ++190 17 67 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++189 17 66 178 16 62 44 4 15 0 0 0 50 5 17 182 16 64 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 72 7 25 ++0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 5 0 2 134 12 47 191 17 67 188 17 66 ++188 17 66 188 17 66 191 17 67 131 12 46 3 0 1 0 0 0 ++0 0 0 101 9 35 190 17 67 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 190 17 67 170 15 60 44 4 15 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 77 7 27 ++183 17 64 189 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++191 17 67 134 12 47 9 1 3 0 0 0 31 3 11 171 15 60 ++189 17 66 188 17 66 188 17 66 188 17 66 188 17 66 72 7 25 ++0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 2 0 1 124 11 43 191 17 67 188 17 66 ++188 17 66 188 17 66 191 17 67 101 9 35 0 0 0 0 0 0 ++0 0 0 35 3 12 168 15 59 190 17 67 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 189 17 66 182 16 64 77 7 27 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 1 2 ++99 9 35 185 17 65 189 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 189 17 66 ++177 16 62 56 5 19 0 0 0 0 0 0 13 1 5 151 14 53 ++190 17 67 188 17 66 188 17 66 188 17 66 185 17 65 56 5 19 ++0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 99 9 35 191 17 67 188 17 66 ++188 17 66 188 17 66 186 17 65 65 6 23 0 0 0 0 0 0 ++0 0 0 0 0 0 79 7 28 182 16 64 190 17 67 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++191 17 67 177 16 62 83 8 29 4 0 1 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++8 1 3 89 8 31 175 16 62 191 17 67 189 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 190 17 67 181 16 63 ++85 8 30 3 0 1 0 0 0 0 0 0 1 0 0 118 13 41 ++191 17 67 188 17 66 188 17 66 189 17 66 173 16 61 34 3 12 ++0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 56 5 19 183 17 64 188 17 66 ++188 17 66 189 17 66 169 15 59 30 3 10 0 0 0 0 0 0 ++0 0 0 0 0 0 5 0 2 83 8 29 173 16 61 191 17 67 ++190 17 67 189 17 66 189 17 66 190 17 67 191 17 67 187 17 66 ++151 14 53 56 5 19 3 0 1 0 0 0 16 1 6 50 5 17 ++79 7 28 95 9 33 95 9 33 75 7 26 41 4 14 10 1 4 ++0 0 0 2 0 1 50 5 17 132 12 46 178 16 62 190 17 67 ++191 17 67 191 17 67 191 17 67 186 17 65 154 14 54 68 6 24 ++4 0 1 0 0 0 0 0 0 0 0 0 0 0 0 72 7 25 ++187 17 66 188 17 66 188 17 66 191 17 67 141 13 49 9 1 3 ++0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 14 1 5 151 14 53 190 17 67 ++188 17 66 191 17 67 131 12 46 5 0 2 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 2 0 1 44 4 15 113 10 40 ++156 14 55 173 16 61 174 16 61 164 15 58 134 12 47 77 7 27 ++18 2 6 0 0 0 16 1 6 85 8 30 151 14 53 182 16 64 ++189 17 66 191 17 67 190 17 67 188 17 66 177 16 62 141 13 49 ++68 6 24 8 1 3 0 0 0 8 1 3 44 4 15 88 8 31 ++113 10 40 122 11 43 108 10 38 67 6 24 20 2 7 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 28 3 10 ++166 15 58 190 17 67 188 17 66 187 17 66 79 7 28 0 0 0 ++0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 73 7 26 185 17 65 ++189 17 66 184 17 65 65 6 23 0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2 0 1 ++17 2 6 32 3 11 34 3 12 22 2 8 6 1 2 0 0 0 ++0 0 0 38 3 13 141 13 49 188 17 66 190 17 67 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 189 17 66 191 17 67 ++184 17 65 122 11 43 21 2 7 0 0 0 0 0 0 0 0 0 ++0 0 0 1 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 1 0 0 ++108 10 38 191 17 67 191 17 67 141 13 49 16 1 6 0 0 0 ++0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 8 1 3 112 10 39 ++186 17 65 124 11 43 10 1 4 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 ++36 3 13 156 14 55 191 17 67 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++189 17 66 190 17 67 134 12 47 18 2 6 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 7 1 2 41 4 14 75 7 26 66 5 23 19 2 7 ++26 2 9 144 13 50 154 14 54 40 4 14 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 13 1 5 ++56 5 19 19 2 7 0 0 0 7 1 2 29 3 10 35 3 12 ++19 2 7 2 0 1 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 13 1 5 ++134 12 47 191 17 67 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 189 17 67 108 10 38 3 0 1 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 ++40 4 14 124 11 43 177 16 62 188 17 66 187 17 66 144 13 50 ++24 2 8 17 2 6 22 2 8 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 19 2 7 122 11 43 171 15 60 175 16 62 ++159 14 56 112 10 39 40 4 14 2 0 1 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 72 7 25 ++186 17 65 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 189 17 66 174 16 61 41 4 14 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 3 0 1 72 7 25 ++168 15 59 191 17 67 189 17 66 188 17 66 188 17 66 190 17 67 ++95 9 33 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 95 9 33 191 17 67 189 17 66 189 17 66 ++190 17 67 191 17 67 171 15 60 90 8 32 12 1 4 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 5 0 2 132 12 46 ++191 17 67 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 190 17 67 98 9 34 0 0 0 ++0 0 0 0 0 0 0 0 0 5 0 2 88 8 31 180 16 63 ++190 17 67 188 17 66 188 17 66 188 17 66 188 17 66 191 17 67 ++146 13 51 11 1 4 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 9 1 3 144 13 50 191 17 67 188 17 66 188 17 66 ++188 17 66 188 17 66 189 17 66 187 17 66 123 11 43 20 2 7 ++0 0 0 0 0 0 0 0 0 0 0 0 21 2 7 163 15 57 ++190 17 67 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 191 17 67 134 12 47 5 0 2 ++0 0 0 0 0 0 3 0 1 88 8 31 182 16 64 189 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 189 17 66 ++171 15 60 31 3 11 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 20 2 7 162 15 57 190 17 67 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 190 17 67 132 12 46 ++20 2 7 0 0 0 0 0 0 0 0 0 32 3 11 173 16 61 ++189 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 190 17 67 151 14 53 12 1 4 ++0 0 0 0 0 0 72 7 25 180 16 63 189 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++181 16 63 47 4 16 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 21 2 7 163 15 57 190 17 67 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 190 17 67 ++122 11 43 9 1 3 0 0 0 0 0 0 30 3 10 171 15 60 ++189 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 190 17 67 146 13 51 10 1 4 ++0 0 0 38 3 13 166 15 58 190 17 67 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++183 17 64 52 5 18 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 13 1 5 154 14 54 190 17 67 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++186 17 65 79 7 28 0 0 0 0 0 0 14 1 5 156 14 54 ++190 17 67 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 191 17 67 124 11 43 2 0 1 ++5 0 2 122 11 43 191 17 67 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++182 16 64 47 4 16 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 3 0 1 126 14 44 191 17 67 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++190 17 67 158 14 55 23 2 8 0 0 0 1 0 0 113 10 40 ++191 17 67 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 78 7 27 0 0 0 ++47 4 16 177 16 62 189 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 189 17 66 ++173 16 61 34 3 12 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 85 8 30 189 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 79 7 28 0 0 0 0 0 0 47 4 16 ++175 16 62 189 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 190 17 67 156 14 55 22 2 8 0 0 0 ++109 10 38 191 17 67 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 190 17 67 ++151 14 53 13 1 5 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 35 3 12 173 16 61 189 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 191 17 67 134 12 47 7 1 2 0 0 0 3 0 1 ++99 9 35 188 17 66 189 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 189 17 66 181 16 63 68 6 24 0 0 0 18 2 6 ++156 14 55 190 17 67 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 190 17 67 ++101 9 35 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 3 0 1 118 13 41 191 17 67 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 189 17 66 168 15 59 28 3 10 0 0 0 0 0 0 ++12 1 4 113 10 40 187 17 66 189 17 67 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++190 17 67 180 16 63 88 8 31 4 0 1 0 0 0 47 4 16 ++180 16 63 189 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 190 17 67 168 15 59 ++36 3 13 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 38 3 13 164 15 58 190 17 67 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 182 16 64 50 5 17 0 0 0 0 0 0 ++0 0 0 11 1 4 90 8 32 169 15 59 190 17 67 190 17 67 ++189 17 66 189 17 66 189 17 66 189 17 66 191 17 67 189 17 66 ++158 14 55 68 6 24 4 0 1 0 0 0 0 0 0 73 7 26 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 189 17 66 185 17 65 83 8 29 ++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 0 0 0 0 0 0 65 6 23 174 16 61 ++190 17 67 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 185 17 65 56 5 19 0 0 0 0 0 0 ++0 0 0 0 0 0 2 0 1 35 3 12 99 9 35 146 13 51 ++170 15 60 177 16 62 177 16 62 166 15 58 141 13 49 85 8 30 ++24 2 8 0 0 0 0 0 0 0 0 0 0 0 0 85 8 30 ++190 17 67 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 189 17 66 112 10 39 8 1 3 ++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 0 0 0 0 0 0 1 0 0 68 6 24 ++170 15 60 191 17 67 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 182 16 64 50 5 17 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 11 1 4 ++28 3 10 40 4 14 38 3 13 25 2 9 8 1 3 0 0 0 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 78 7 27 ++189 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 189 17 66 187 17 66 113 10 40 14 1 5 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 0 0 0 0 0 0 0 0 0 0 0 0 1 0 0 ++47 4 16 141 13 49 186 17 65 191 17 67 190 17 67 189 17 66 ++189 17 66 191 17 67 156 14 55 20 2 7 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 0 0 0 0 0 0 44 4 15 ++178 16 62 190 17 67 188 17 66 188 17 66 188 17 66 190 17 67 ++191 17 67 173 16 61 90 8 32 10 1 4 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 ++0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 ++0 0 0 14 1 5 68 6 24 131 12 46 162 15 57 174 16 61 ++171 15 60 146 13 51 56 5 19 0 0 0 0 0 0 0 0 0 ++0 0 0 0 0 0 0 0 0 3 0 1 14 1 5 29 3 10 ++41 4 14 47 4 16 50 5 17 45 4 16 34 3 12 18 2 6 ++5 0 2 0 0 0 0 0 0 0 0 0 0 0 0 5 0 2 ++90 8 32 169 15 59 185 17 65 187 17 66 182 16 64 163 15 57 ++113 10 40 41 4 14 2 0 1 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 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 5 0 2 21 2 7 34 3 12 ++29 3 10 11 1 4 0 0 0 0 0 0 0 0 0 0 0 0 ++3 0 1 32 3 11 79 7 28 124 11 43 154 14 54 171 15 60 ++180 16 63 182 16 64 182 16 64 180 16 63 174 16 61 159 14 56 ++132 12 46 88 8 31 34 3 12 3 0 1 0 0 0 0 0 0 ++3 0 1 29 3 10 56 5 19 65 6 23 50 5 17 23 2 8 ++3 0 1 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 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 0 0 0 0 0 0 0 0 0 25 2 9 ++109 10 38 169 15 59 189 17 66 191 17 67 190 17 67 189 17 66 ++189 17 66 188 17 66 188 17 66 188 17 66 189 17 66 190 17 67 ++191 17 67 190 17 67 171 15 60 98 9 34 10 1 3 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 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 ++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 14 1 5 141 13 49 ++191 17 67 189 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 189 17 67 186 17 65 65 6 23 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 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 ++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 23 2 8 166 15 58 ++190 17 67 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 189 17 66 176 16 62 45 4 16 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 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 ++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 1 0 0 83 8 29 ++183 17 64 189 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++188 17 66 189 17 66 185 17 65 95 9 33 3 0 1 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 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 ++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 5 0 2 ++85 8 30 176 16 62 191 17 67 188 17 66 188 17 66 188 17 66 ++188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 188 17 66 ++191 17 67 180 16 63 95 9 33 7 1 3 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 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 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 ++2 0 1 52 5 18 141 13 49 185 17 65 191 17 67 189 17 67 ++189 17 66 188 17 66 188 17 66 189 17 66 191 17 67 187 17 66 ++146 13 51 56 5 19 4 0 1 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 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 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 14 1 5 68 6 24 131 12 46 166 15 58 ++180 16 63 183 17 64 180 16 63 168 15 59 134 12 47 75 7 26 ++17 2 6 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 ++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 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 0 0 0 0 0 0 0 0 0 5 0 2 24 2 8 ++44 4 15 52 5 18 45 4 16 26 2 9 6 1 2 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 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 ++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 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 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 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 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 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 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 0 0 0 +-- +1.7.5.4 + diff --git a/target/linux/brcm2708/patches-3.3/0005-bcm2708-vchiq-driver.patch b/target/linux/brcm2708/patches-3.3/0005-bcm2708-vchiq-driver.patch new file mode 100644 index 0000000000..bee41d712e --- /dev/null +++ b/target/linux/brcm2708/patches-3.3/0005-bcm2708-vchiq-driver.patch @@ -0,0 +1,16383 @@ +From a2e42fbc97cde9e851f5b036f46b8f34a5be6eb9 Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Tue, 17 Jan 2012 19:22:19 +0000 +Subject: [PATCH 5/7] bcm2708 vchiq driver + +Signed-off-by: popcornmix <popcornmix@gmail.com> +--- + drivers/misc/Kconfig | 1 + + drivers/misc/Makefile | 1 + + drivers/misc/vc04_services/Kconfig | 7 + + drivers/misc/vc04_services/Makefile | 19 + + .../misc/vc04_services/interface/vchi/vchi_mh.h | 19 + + .../misc/vc04_services/interface/vchiq_arm/vchiq.h | 27 + + .../vc04_services/interface/vchiq_arm/vchiq_2835.h | 27 + + .../interface/vchiq_arm/vchiq_2835_arm.c | 487 ++++ + .../vc04_services/interface/vchiq_arm/vchiq_arm.c | 1293 ++++++++++ + .../vc04_services/interface/vchiq_arm/vchiq_arm.h | 38 + + .../vc04_services/interface/vchiq_arm/vchiq_cfg.h | 43 + + .../interface/vchiq_arm/vchiq_connected.c | 101 + + .../interface/vchiq_arm/vchiq_connected.h | 32 + + .../vc04_services/interface/vchiq_arm/vchiq_core.c | 2604 ++++++++++++++++++++ + .../vc04_services/interface/vchiq_arm/vchiq_core.h | 480 ++++ + .../vc04_services/interface/vchiq_arm/vchiq_if.h | 148 ++ + .../interface/vchiq_arm/vchiq_ioctl.h | 105 + + .../interface/vchiq_arm/vchiq_kern_lib.c | 297 +++ + .../vc04_services/interface/vchiq_arm/vchiq_lib.c | 1518 ++++++++++++ + .../interface/vchiq_arm/vchiq_memdrv.h | 45 + + .../interface/vchiq_arm/vchiq_pagelist.h | 43 + + .../vc04_services/interface/vchiq_arm/vchiq_shim.c | 970 ++++++++ + .../vc04_services/interface/vchiq_arm/vchiq_util.c | 97 + + .../vc04_services/interface/vchiq_arm/vchiq_util.h | 47 + + .../interface/vcos/generic/vcos_cmd.c | 681 +++++ + .../interface/vcos/generic/vcos_common.h | 76 + + .../vcos/generic/vcos_generic_blockpool.h | 260 ++ + .../vcos/generic/vcos_generic_event_flags.c | 297 +++ + .../vcos/generic/vcos_generic_event_flags.h | 104 + + .../vcos/generic/vcos_generic_named_sem.h | 81 + + .../vcos/generic/vcos_generic_quickslow_mutex.h | 75 + + .../vcos/generic/vcos_generic_reentrant_mtx.h | 75 + + .../interface/vcos/generic/vcos_generic_tls.h | 144 ++ + .../vcos/generic/vcos_joinable_thread_from_plain.h | 202 ++ + .../interface/vcos/generic/vcos_latch_from_sem.h | 48 + + .../interface/vcos/generic/vcos_logcat.c | 549 +++++ + .../interface/vcos/generic/vcos_mem_from_malloc.c | 73 + + .../interface/vcos/generic/vcos_mem_from_malloc.h | 54 + + .../vcos/generic/vcos_mutexes_are_reentrant.h | 68 + + .../interface/vcos/generic/vcos_thread_reaper.h | 35 + + .../interface/vcos/linuxkernel/stdint.h | 17 + + .../interface/vcos/linuxkernel/vcos_linuxkernel.c | 616 +++++ + .../vcos/linuxkernel/vcos_linuxkernel_cfg.c | 332 +++ + .../vcos/linuxkernel/vcos_linuxkernel_misc.c | 113 + + .../interface/vcos/linuxkernel/vcos_mod_init.c | 64 + + .../interface/vcos/linuxkernel/vcos_platform.h | 496 ++++ + .../vcos/linuxkernel/vcos_platform_types.h | 47 + + .../interface/vcos/linuxkernel/vcos_thread_map.c | 129 + + .../interface/vcos/linuxkernel/vcos_thread_map.h | 39 + + drivers/misc/vc04_services/interface/vcos/vcos.h | 201 ++ + .../vc04_services/interface/vcos/vcos_assert.h | 269 ++ + .../interface/vcos/vcos_atomic_flags.h | 72 + + .../vc04_services/interface/vcos/vcos_build_info.h | 5 + + .../misc/vc04_services/interface/vcos/vcos_cfg.h | 113 + + .../misc/vc04_services/interface/vcos/vcos_cmd.h | 98 + + .../misc/vc04_services/interface/vcos/vcos_ctype.h | 29 + + .../misc/vc04_services/interface/vcos/vcos_dlfcn.h | 69 + + .../misc/vc04_services/interface/vcos/vcos_event.h | 97 + + .../interface/vcos/vcos_event_flags.h | 98 + + .../misc/vc04_services/interface/vcos/vcos_init.h | 43 + + .../vc04_services/interface/vcos/vcos_logging.h | 279 +++ + .../interface/vcos/vcos_lowlevel_thread.h | 107 + + .../misc/vc04_services/interface/vcos/vcos_mem.h | 81 + + .../vc04_services/interface/vcos/vcos_msgqueue.h | 157 ++ + .../misc/vc04_services/interface/vcos/vcos_mutex.h | 92 + + .../misc/vc04_services/interface/vcos/vcos_once.h | 42 + + .../vc04_services/interface/vcos/vcos_semaphore.h | 115 + + .../vc04_services/interface/vcos/vcos_stdbool.h | 17 + + .../vc04_services/interface/vcos/vcos_stdint.h | 193 ++ + .../vc04_services/interface/vcos/vcos_string.h | 73 + + .../vc04_services/interface/vcos/vcos_thread.h | 259 ++ + .../interface/vcos/vcos_thread_attr.h | 73 + + .../misc/vc04_services/interface/vcos/vcos_timer.h | 95 + + .../misc/vc04_services/interface/vcos/vcos_types.h | 197 ++ + 74 files changed, 15998 insertions(+), 0 deletions(-) + create mode 100644 drivers/misc/vc04_services/Kconfig + create mode 100644 drivers/misc/vc04_services/Makefile + create mode 100644 drivers/misc/vc04_services/interface/vchi/vchi_mh.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_lib.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c + create mode 100644 drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_cmd.c + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_common.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_blockpool.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.c + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_named_sem.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_quickslow_mutex.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_reentrant_mtx.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_tls.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_joinable_thread_from_plain.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_latch_from_sem.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_logcat.c + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.c + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_mutexes_are_reentrant.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/generic/vcos_thread_reaper.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/stdint.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel.c + create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel_cfg.c + create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel_misc.c + create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_mod_init.c + create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform_types.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_thread_map.c + create mode 100644 drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_thread_map.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_assert.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_atomic_flags.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_build_info.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_cfg.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_cmd.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_ctype.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_dlfcn.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_event.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_event_flags.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_init.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_logging.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_lowlevel_thread.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_mem.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_msgqueue.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_mutex.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_once.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_semaphore.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_stdbool.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_stdint.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_string.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_thread.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_thread_attr.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_timer.h + create mode 100644 drivers/misc/vc04_services/interface/vcos/vcos_types.h + +--- a/drivers/misc/Kconfig ++++ b/drivers/misc/Kconfig +@@ -506,4 +506,5 @@ source "drivers/misc/ti-st/Kconfig" + source "drivers/misc/lis3lv02d/Kconfig" + source "drivers/misc/carma/Kconfig" + source "drivers/misc/altera-stapl/Kconfig" ++source "drivers/misc/vc04_services/Kconfig" + endmenu +--- a/drivers/misc/Makefile ++++ b/drivers/misc/Makefile +@@ -49,3 +49,5 @@ obj-y += carma/ + obj-$(CONFIG_USB_SWITCH_FSA9480) += fsa9480.o + obj-$(CONFIG_ALTERA_STAPL) +=altera-stapl/ + obj-$(CONFIG_MAX8997_MUIC) += max8997-muic.o ++obj-y += vc04_services/ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/Kconfig +@@ -0,0 +1,7 @@ ++config BCM2708_VCHIQ ++ tristate "Videocore VCHIQ" ++ depends on MACH_BCM2708 ++ default y ++ help ++ Helper for communication for VideoCore. ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/Makefile +@@ -0,0 +1,19 @@ ++obj-$(CONFIG_BCM2708_VCHIQ) += vchiq.o ++ ++vchiq-objs := \ ++ interface/vchiq_arm/vchiq_core.o \ ++ interface/vchiq_arm/vchiq_arm.o \ ++ interface/vchiq_arm/vchiq_kern_lib.o \ ++ interface/vchiq_arm/vchiq_2835_arm.o \ ++ interface/vcos/linuxkernel/vcos_linuxkernel.o \ ++ interface/vcos/linuxkernel/vcos_thread_map.o \ ++ interface/vcos/linuxkernel/vcos_linuxkernel_cfg.o \ ++ interface/vcos/generic/vcos_generic_event_flags.o \ ++ interface/vcos/generic/vcos_logcat.o \ ++ interface/vcos/generic/vcos_mem_from_malloc.o \ ++ interface/vcos/generic/vcos_cmd.o ++ ++EXTRA_CFLAGS += -DVCOS_VERIFY_BKPTS=1 -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel ++ ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi_mh.h +@@ -0,0 +1,19 @@ ++/*============================================================================= ++Copyright (c) 2010 Broadcom Europe Limited. All rights reserved. ++ ++Project : vchi ++Module : vchi ++ ++FILE DESCRIPTION: ++Definitions for memory handle types. ++=============================================================================*/ ++ ++#ifndef VCHI_MH_H_ ++#define VCHI_MH_H_ ++ ++#include <interface/vcos/vcos.h> ++ ++typedef int32_t VCHI_MEM_HANDLE_T; ++#define VCHI_MEM_HANDLE_INVALID 0 ++ ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq.h +@@ -0,0 +1,27 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef VCHIQ_VCHIQ_H ++#define VCHIQ_VCHIQ_H ++ ++#include "vchiq_if.h" ++#include "vchiq_util.h" ++#include "interface/vcos/vcos.h" ++ ++#endif ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835.h +@@ -0,0 +1,27 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef VCHIQ_2835_H ++#define VCHIQ_2835_H ++ ++#include "vchiq_pagelist.h" ++ ++#define VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX 0 ++#define VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX 1 ++ ++#endif /* VCHIQ_2835_H */ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +@@ -0,0 +1,487 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/types.h> ++#include <linux/errno.h> ++#include <linux/interrupt.h> ++#include <linux/irq.h> ++#include <linux/pagemap.h> ++#include <linux/dma-mapping.h> ++#include <linux/version.h> ++#include <asm/pgtable.h> ++#include <asm/io.h> ++#include <asm/uaccess.h> ++ ++#include <mach/irqs.h> ++ ++#include <mach/platform.h> ++#include <mach/vcio.h> ++ ++#define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32) ++ ++#define VCHIQ_DOORBELL_IRQ IRQ_ARM_DOORBELL_0 ++#define VCHIQ_ARM_ADDRESS(x) __virt_to_bus(x) ++ ++#include "vchiq_arm.h" ++#include "vchiq_2835.h" ++ ++#define MAX_FRAGMENTS (VCHIQ_NUM_CURRENT_BULKS * 2) ++ ++#define VCOS_LOG_CATEGORY (&vchiq_arm_log_category) ++ ++static char *g_slot_mem; ++static int g_slot_mem_size; ++dma_addr_t g_slot_phys; ++static FRAGMENTS_T *g_fragments_base; ++static FRAGMENTS_T *g_free_fragments; ++struct semaphore g_free_fragments_sema; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) ++static DEFINE_SEMAPHORE(g_free_fragments_mutex); ++#else ++static DECLARE_MUTEX(g_free_fragments_mutex); ++#endif ++ ++static irqreturn_t ++vchiq_doorbell_irq(int irq, void *dev_id); ++ ++static int ++create_pagelist(char __user *buf, size_t count, unsigned short type, ++ struct task_struct *task, PAGELIST_T ** ppagelist); ++ ++static void ++free_pagelist(PAGELIST_T *pagelist, int actual); ++ ++int __init ++vchiq_platform_vcos_init(void) ++{ ++ return (vcos_init() == VCOS_SUCCESS) ? 0 : -EINVAL; ++} ++ ++int __init ++vchiq_platform_init(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_SLOT_ZERO_T *vchiq_slot_zero; ++ int frag_mem_size; ++ int err; ++ int i; ++ ++ /* Allocate space for the channels in coherent memory */ ++ g_slot_mem_size = PAGE_ALIGN(TOTAL_SLOTS * VCHIQ_SLOT_SIZE); ++ frag_mem_size = PAGE_ALIGN(sizeof(FRAGMENTS_T) * MAX_FRAGMENTS); ++ ++ g_slot_mem = dma_alloc_coherent(NULL, g_slot_mem_size + frag_mem_size, ++ &g_slot_phys, GFP_ATOMIC); ++ ++ if (!g_slot_mem) { ++ vcos_log_error("Unable to allocate channel memory"); ++ err = -ENOMEM; ++ goto failed_alloc; ++ } ++ ++ vcos_assert(((int)g_slot_mem & (PAGE_SIZE - 1)) == 0); ++ ++ vchiq_slot_zero = vchiq_init_slots(g_slot_mem, g_slot_mem_size); ++ if (!vchiq_slot_zero) ++ { ++ err = -EINVAL; ++ goto failed_init_slots; ++ } ++ ++ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_OFFSET_IDX] = (int)g_slot_phys + g_slot_mem_size; ++ vchiq_slot_zero->platform_data[VCHIQ_PLATFORM_FRAGMENTS_COUNT_IDX] = MAX_FRAGMENTS; ++ ++ g_fragments_base = (FRAGMENTS_T *)(g_slot_mem + g_slot_mem_size); ++ g_slot_mem_size += frag_mem_size; ++ ++ g_free_fragments = g_fragments_base; ++ for (i = 0; i < (MAX_FRAGMENTS - 1); i++) { ++ *(FRAGMENTS_T **) & g_fragments_base[i] = ++ &g_fragments_base[i + 1]; ++ } ++ *(FRAGMENTS_T **) & g_fragments_base[i] = NULL; ++ sema_init(&g_free_fragments_sema, MAX_FRAGMENTS); ++ ++ if (vchiq_init_state(state, vchiq_slot_zero, 0/*slave*/) != ++ VCHIQ_SUCCESS) ++ { ++ err = -EINVAL; ++ goto failed_vchiq_init; ++ } ++ ++ err = request_irq(VCHIQ_DOORBELL_IRQ, vchiq_doorbell_irq, ++ IRQF_SAMPLE_RANDOM | IRQF_IRQPOLL, "VCHIQ doorbell", ++ state); ++ if (err < 0) ++ { ++ printk( KERN_ERR "%s: failed to register irq=%d err=%d\n", __func__, ++ VCHIQ_DOORBELL_IRQ, err ); ++ goto failed_request_irq; ++ } ++ ++ /* Send the base address of the slots to VideoCore */ ++ ++ dsb(); /* Ensure all writes have completed */ ++ ++ bcm_mailbox_write(MBOX_CHAN_VCHIQ, (unsigned int)g_slot_phys); ++ ++ vcos_log_info("vchiq_init - done (slots %x, phys %x)", ++ (unsigned int)vchiq_slot_zero, g_slot_phys); ++ ++ return 0; ++ ++failed_request_irq: ++failed_vchiq_init: ++failed_init_slots: ++ dma_free_coherent(NULL, g_slot_mem_size, g_slot_mem, g_slot_phys); ++ ++failed_alloc: ++ return err; ++} ++ ++void __exit ++vchiq_platform_exit(VCHIQ_STATE_T *state) ++{ ++ free_irq(VCHIQ_DOORBELL_IRQ, state); ++ dma_free_coherent(NULL, g_slot_mem_size, ++ g_slot_mem, g_slot_phys); ++} ++ ++void ++remote_event_signal(REMOTE_EVENT_T *event) ++{ ++ event->fired = 1; ++ ++ /* The test on the next line also ensures the write on the previous line ++ has completed */ ++ ++ if (event->armed) { ++ /* trigger vc interrupt */ ++ dsb(); /* data barrier operation */ ++ ++ writel(0, __io_address(ARM_0_BELL2)); ++ } ++} ++ ++int ++vchiq_copy_from_user(void *dst, const void *src, int size) ++{ ++ return copy_from_user(dst, src, size); ++} ++ ++VCHIQ_STATUS_T ++vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, VCHI_MEM_HANDLE_T memhandle, ++ void *offset, int size, int dir) ++{ ++ PAGELIST_T *pagelist; ++ int ret; ++ ++ vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID); ++ ++ ret = create_pagelist((char __user *)offset, size, ++ (dir == VCHIQ_BULK_RECEIVE) ++ ? PAGELIST_READ ++ : PAGELIST_WRITE, ++ current, ++ &pagelist); ++ if (ret != 0) ++ return VCHIQ_ERROR; ++ ++ bulk->handle = memhandle; ++ bulk->data = VCHIQ_ARM_ADDRESS(pagelist); ++ ++ /* Store the pagelist address in remote_data, which isn't used by the ++ slave. */ ++ bulk->remote_data = pagelist; ++ ++ return VCHIQ_SUCCESS; ++} ++ ++void ++vchiq_complete_bulk(VCHIQ_BULK_T *bulk) ++{ ++ free_pagelist((PAGELIST_T *)bulk->remote_data, bulk->actual); ++} ++ ++void ++vchiq_transfer_bulk(VCHIQ_BULK_T *bulk) ++{ ++ /* ++ * This should only be called on the master (VideoCore) side, but ++ * provide an implementation to avoid the need for ifdefery. ++ */ ++ vcos_assert(!"This code should not be called by the ARM on BCM2835"); ++} ++ ++void ++vchiq_dump_platform_state(void *dump_context) ++{ ++ char buf[80]; ++ int len; ++ len = vcos_snprintf(buf, sizeof(buf), ++ " Platform: 2835 (VC master)"); ++ vchiq_dump(dump_context, buf, len + 1); ++} ++ ++void ++vchiq_platform_paused(VCHIQ_STATE_T *state) ++{ ++ vcos_unused(state); ++ vcos_assert_msg(0, "Suspend/resume not supported"); ++} ++ ++void ++vchiq_platform_resumed(VCHIQ_STATE_T *state) ++{ ++ vcos_unused(state); ++ vcos_assert_msg(0, "Suspend/resume not supported"); ++} ++ ++VCHIQ_STATUS_T ++vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ if (!service) ++ return VCHIQ_ERROR; ++ return VCHIQ_SUCCESS; ++} ++ ++VCHIQ_STATUS_T ++vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ if (!service) ++ return VCHIQ_ERROR; ++ return VCHIQ_SUCCESS; ++} ++ ++VCHIQ_STATUS_T ++vchiq_check_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ if (!service) ++ return VCHIQ_ERROR; ++ return VCHIQ_SUCCESS; ++} ++ ++/* ++ * Local functions ++ */ ++ ++static irqreturn_t ++vchiq_doorbell_irq(int irq, void *dev_id) ++{ ++ VCHIQ_STATE_T *state = dev_id; ++ irqreturn_t ret = IRQ_NONE; ++ unsigned int status; ++ ++ /* Read (and clear) the doorbell */ ++ status = readl(__io_address(ARM_0_BELL0)); ++ ++ if (status & 0x4) { /* Was the doorbell rung? */ ++ remote_event_pollall(state); ++ ret = IRQ_HANDLED; ++ } ++ ++ return ret; ++} ++ ++/* There is a potential problem with partial cache lines (pages?) ++ at the ends of the block when reading. If the CPU accessed anything in ++ the same line (page?) then it may have pulled old data into the cache, ++ obscuring the new data underneath. We can solve this by transferring the ++ partial cache lines separately, and allowing the ARM to copy into the ++ cached area. ++ ++ N.B. This implementation plays slightly fast and loose with the Linux ++ driver programming rules, e.g. its use of __virt_to_bus instead of ++ dma_map_single, but it isn't a multi-platform driver and it benefits ++ from increased speed as a result. ++ */ ++ ++static int ++create_pagelist(char __user *buf, size_t count, unsigned short type, ++ struct task_struct *task, PAGELIST_T ** ppagelist) ++{ ++ PAGELIST_T *pagelist; ++ struct page **pages; ++ struct page *page; ++ unsigned long *addrs; ++ unsigned int num_pages, offset, i; ++ char *addr, *base_addr, *next_addr; ++ int run, addridx, actual_pages; ++ ++ offset = (unsigned int)buf & (PAGE_SIZE - 1); ++ num_pages = (count + offset + PAGE_SIZE - 1) / PAGE_SIZE; ++ ++ *ppagelist = NULL; ++ ++ /* Allocate enough storage to hold the page pointers and the page list */ ++ pagelist = (PAGELIST_T *) kmalloc(sizeof(PAGELIST_T) + ++ (num_pages * sizeof(unsigned long)) + ++ (num_pages * sizeof(pages[0])), ++ GFP_KERNEL); ++ ++ vcos_log_trace("create_pagelist - %x", (unsigned int)pagelist); ++ if (!pagelist) ++ return -ENOMEM; ++ ++ addrs = pagelist->addrs; ++ pages = (struct page **)(addrs + num_pages); ++ ++ down_read(&task->mm->mmap_sem); ++ actual_pages = get_user_pages(task, task->mm, ++ (unsigned long)buf & ~(PAGE_SIZE - 1), num_pages, ++ (type == PAGELIST_READ) /*Write */ , 0 /*Force */ , ++ pages, NULL /*vmas */ ); ++ up_read(&task->mm->mmap_sem); ++ ++ if (actual_pages != num_pages) ++ { ++ for (i = 0; i < actual_pages; i++) { ++ page_cache_release(pages[i]); ++ } ++ kfree(pagelist); ++ return -EINVAL; ++ } ++ ++ pagelist->length = count; ++ pagelist->type = type; ++ pagelist->offset = offset; ++ ++ /* Group the pages into runs of contiguous pages */ ++ ++ base_addr = VCHIQ_ARM_ADDRESS(page_address(pages[0])); ++ next_addr = base_addr + PAGE_SIZE; ++ addridx = 0; ++ run = 0; ++ ++ for (i = 1; i < num_pages; i++) { ++ addr = VCHIQ_ARM_ADDRESS(page_address(pages[i])); ++ if ((addr == next_addr) && (run < (PAGE_SIZE - 1))) { ++ next_addr += PAGE_SIZE; ++ run++; ++ } else { ++ addrs[addridx] = (unsigned long)base_addr + run; ++ addridx++; ++ base_addr = addr; ++ next_addr = addr + PAGE_SIZE; ++ run = 0; ++ } ++ } ++ ++ addrs[addridx] = (unsigned long)base_addr + run; ++ addridx++; ++ ++ /* Partial cache lines (fragments) require special measures */ ++ if ((type == PAGELIST_READ) && ++ ((pagelist->offset & (CACHE_LINE_SIZE - 1)) || ++ ((pagelist->offset + pagelist->length) & (CACHE_LINE_SIZE - 1)))) { ++ FRAGMENTS_T *fragments; ++ ++ if (down_interruptible(&g_free_fragments_sema) != 0) { ++ kfree(pagelist); ++ return -EINTR; ++ } ++ ++ vcos_assert(g_free_fragments != NULL); ++ ++ down(&g_free_fragments_mutex); ++ fragments = (FRAGMENTS_T *) g_free_fragments; ++ vcos_assert(fragments != NULL); ++ g_free_fragments = *(FRAGMENTS_T **) g_free_fragments; ++ up(&g_free_fragments_mutex); ++ pagelist->type = ++ PAGELIST_READ_WITH_FRAGMENTS + (fragments - ++ g_fragments_base); ++ } ++ ++ for (page = virt_to_page(pagelist); ++ page <= virt_to_page(addrs + num_pages - 1); page++) { ++ flush_dcache_page(page); ++ } ++ ++ *ppagelist = pagelist; ++ ++ return 0; ++} ++ ++static void ++free_pagelist(PAGELIST_T *pagelist, int actual) ++{ ++ struct page **pages; ++ unsigned int num_pages, i; ++ ++ vcos_log_trace("free_pagelist - %x, %d", (unsigned int)pagelist, actual); ++ ++ num_pages = ++ (pagelist->length + pagelist->offset + PAGE_SIZE - 1) / PAGE_SIZE; ++ ++ pages = (struct page **)(pagelist->addrs + num_pages); ++ ++ /* Deal with any partial cache lines (fragments) */ ++ if (pagelist->type >= PAGELIST_READ_WITH_FRAGMENTS) { ++ FRAGMENTS_T *fragments = ++ g_fragments_base + (pagelist->type - ++ PAGELIST_READ_WITH_FRAGMENTS); ++ int head_bytes, tail_bytes; ++ ++ if (actual >= 0) ++ { ++ if ((head_bytes = (CACHE_LINE_SIZE - pagelist->offset) & (CACHE_LINE_SIZE - 1)) != 0) { ++ if (head_bytes > actual) ++ head_bytes = actual; ++ ++ memcpy((char *)page_address(pages[0]) + ++ pagelist->offset, fragments->headbuf, ++ head_bytes); ++ } ++ if ((head_bytes < actual) && ++ (tail_bytes = ++ (pagelist->offset + actual) & (CACHE_LINE_SIZE - ++ 1)) != 0) { ++ memcpy((char *)page_address(pages[num_pages - 1]) + ++ ((pagelist->offset + actual) & (PAGE_SIZE - ++ 1) & ~(CACHE_LINE_SIZE - 1)), ++ fragments->tailbuf, tail_bytes); ++ } ++ } ++ ++ down(&g_free_fragments_mutex); ++ *(FRAGMENTS_T **) fragments = g_free_fragments; ++ g_free_fragments = fragments; ++ up(&g_free_fragments_mutex); ++ up(&g_free_fragments_sema); ++ } ++ ++ for (i = 0; i < num_pages; i++) { ++ if (pagelist->type != PAGELIST_WRITE) ++ set_page_dirty(pages[i]); ++ page_cache_release(pages[i]); ++ } ++ ++ kfree(pagelist); ++} ++ ++VCHIQ_STATUS_T ++vchiq_platform_suspend(VCHIQ_STATE_T *state) ++{ ++ vcos_unused(state); ++ return VCHIQ_ERROR; ++} +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -0,0 +1,1293 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/errno.h> ++#include <linux/cdev.h> ++#include <linux/fs.h> ++#include <linux/device.h> ++ ++#include "vchiq_core.h" ++#include "vchiq_ioctl.h" ++#include "vchiq_arm.h" ++ ++#define DEVICE_NAME "vchiq" ++ ++/* Override the default prefix, which would be vchiq_arm (from the filename) */ ++#undef MODULE_PARAM_PREFIX ++#define MODULE_PARAM_PREFIX DEVICE_NAME "." ++ ++#define VCHIQ_MINOR 0 ++ ++/* Some per-instance constants */ ++#define MAX_COMPLETIONS 16 ++#define MAX_SERVICES 64 ++#define MAX_ELEMENTS 8 ++#define MSG_QUEUE_SIZE 64 ++ ++#define VCOS_LOG_CATEGORY (&vchiq_arm_log_category) ++ ++typedef struct client_service_struct { ++ VCHIQ_SERVICE_T *service; ++ void *userdata; ++ VCHIQ_INSTANCE_T instance; ++ int handle; ++ int is_vchi; ++ volatile int dequeue_pending; ++ volatile int message_available_pos; ++ volatile int msg_insert; ++ volatile int msg_remove; ++ VCOS_EVENT_T insert_event; ++ VCOS_EVENT_T remove_event; ++ VCHIQ_HEADER_T *msg_queue[MSG_QUEUE_SIZE]; ++} USER_SERVICE_T; ++ ++struct vchiq_instance_struct { ++ VCHIQ_STATE_T *state; ++ VCHIQ_COMPLETION_DATA_T completions[MAX_COMPLETIONS]; ++ volatile int completion_insert; ++ volatile int completion_remove; ++ VCOS_EVENT_T insert_event; ++ VCOS_EVENT_T remove_event; ++ ++ USER_SERVICE_T services[MAX_SERVICES]; ++ ++ int connected; ++ int closing; ++ int pid; ++ int mark; ++}; ++ ++typedef struct dump_context_struct ++{ ++ char __user *buf; ++ size_t actual; ++ size_t space; ++ loff_t offset; ++} DUMP_CONTEXT_T; ++ ++VCOS_LOG_CAT_T vchiq_arm_log_category; ++ ++static struct cdev vchiq_cdev; ++static dev_t vchiq_devid; ++static VCHIQ_STATE_T g_state; ++static struct class *vchiq_class; ++static struct device *vchiq_dev; ++ ++static const char *ioctl_names[] = ++{ ++ "CONNECT", ++ "SHUTDOWN", ++ "CREATE_SERVICE", ++ "REMOVE_SERVICE", ++ "QUEUE_MESSAGE", ++ "QUEUE_BULK_TRANSMIT", ++ "QUEUE_BULK_RECEIVE", ++ "AWAIT_COMPLETION", ++ "DEQUEUE_MESSAGE", ++ "GET_CLIENT_ID", ++ "GET_CONFIG", ++ "CLOSE_SERVICE", ++ "USE_SERVICE", ++ "RELEASE_SERIVCE" ++}; ++ ++VCOS_LOG_LEVEL_T vchiq_default_arm_log_level = VCOS_LOG_WARN; ++ ++/**************************************************************************** ++* ++* find_service_by_handle ++* ++***************************************************************************/ ++ ++static inline USER_SERVICE_T *find_service_by_handle( ++ VCHIQ_INSTANCE_T instance, int handle ) ++{ ++ USER_SERVICE_T *user_service; ++ ++ if (( handle >= 0 ) ++ && ( handle < MAX_SERVICES )) ++ { ++ user_service = &instance->services[ handle ]; ++ ++ if ( user_service->service != NULL ) ++ { ++ return user_service; ++ } ++ } ++ ++ return NULL; ++} ++ ++/**************************************************************************** ++* ++* find_avail_service_handle ++* ++***************************************************************************/ ++ ++static inline USER_SERVICE_T *find_avail_service_handle( ++ VCHIQ_INSTANCE_T instance) ++{ ++ int handle; ++ ++ for ( handle = 0; handle < MAX_SERVICES; handle++ ) ++ { ++ if ( instance->services[handle].service == NULL ) ++ { ++ instance->services[handle].instance = instance; ++ instance->services[handle].handle = handle; ++ ++ return &instance->services[handle]; ++ } ++ } ++ return NULL; ++} ++ ++/**************************************************************************** ++* ++* add_completion ++* ++***************************************************************************/ ++ ++static VCHIQ_STATUS_T ++add_completion(VCHIQ_INSTANCE_T instance, VCHIQ_REASON_T reason, ++ VCHIQ_HEADER_T *header, USER_SERVICE_T *service, void *bulk_userdata) ++{ ++ VCHIQ_COMPLETION_DATA_T *completion; ++ DEBUG_INITIALISE(g_state.local) ++ ++ while (instance->completion_insert == ++ (instance->completion_remove + MAX_COMPLETIONS)) { ++ /* Out of space - wait for the client */ ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ vcos_log_trace("add_completion - completion queue full"); ++ DEBUG_COUNT(COMPLETION_QUEUE_FULL_COUNT); ++ if (vcos_event_wait(&instance->remove_event) != VCOS_SUCCESS) { ++ vcos_log_info("service_callback interrupted"); ++ return VCHIQ_RETRY; ++ } else if (instance->closing) { ++ vcos_log_info("service_callback closing"); ++ return VCHIQ_ERROR; ++ } ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ } ++ ++ completion = ++ &instance-> ++ completions[instance->completion_insert & (MAX_COMPLETIONS - 1)]; ++ ++ completion->header = header; ++ completion->reason = reason; ++ completion->service_userdata = service; ++ completion->bulk_userdata = bulk_userdata; ++ ++ /* A write barrier is needed here to ensure that the entire completion ++ record is written out before the insert point. */ ++ vcos_wmb(&completion->bulk_userdata); ++ ++ if (reason == VCHIQ_MESSAGE_AVAILABLE) ++ service->message_available_pos = instance->completion_insert; ++ instance->completion_insert++; ++ ++ vcos_event_signal(&instance->insert_event); ++ ++ return VCHIQ_SUCCESS; ++} ++ ++/**************************************************************************** ++* ++* service_callback ++* ++***************************************************************************/ ++ ++static VCHIQ_STATUS_T ++service_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, ++ VCHIQ_SERVICE_HANDLE_T handle, void *bulk_userdata) ++{ ++ /* How do we ensure the callback goes to the right client? ++ The service_user data points to a USER_SERVICE_T record containing the ++ original callback and the user state structure, which contains a circular ++ buffer for completion records. ++ */ ++ USER_SERVICE_T *service = ++ (USER_SERVICE_T *) VCHIQ_GET_SERVICE_USERDATA(handle); ++ VCHIQ_INSTANCE_T instance = service->instance; ++ DEBUG_INITIALISE(g_state.local) ++ ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ vcos_log_trace ++ ("service_callback - service %lx(%d), reason %d, header %lx, " ++ "instance %lx, bulk_userdata %lx", ++ (unsigned long)service, ((VCHIQ_SERVICE_T *) handle)->localport, ++ reason, (unsigned long)header, ++ (unsigned long)instance, (unsigned long)bulk_userdata); ++ ++ if (!instance || instance->closing) { ++ return VCHIQ_SUCCESS; ++ } ++ ++ if (header && service->is_vchi) ++ { ++ while (service->msg_insert == (service->msg_remove + MSG_QUEUE_SIZE)) ++ { ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ DEBUG_COUNT(MSG_QUEUE_FULL_COUNT); ++ vcos_log_trace("service_callback - msg queue full"); ++ /* If there is no MESSAGE_AVAILABLE in the completion queue, add one */ ++ if ((service->message_available_pos - instance->completion_remove) < 0) ++ { ++ VCHIQ_STATUS_T status; ++ vcos_log_warn("Inserting extra MESSAGE_AVAILABLE"); ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ status = add_completion(instance, reason, NULL, service, bulk_userdata); ++ if (status != VCHIQ_SUCCESS) ++ { ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ return status; ++ } ++ } ++ ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ if (vcos_event_wait(&service->remove_event) != VCOS_SUCCESS) { ++ vcos_log_info("service_callback interrupted"); ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ return VCHIQ_RETRY; ++ } else if (instance->closing) { ++ vcos_log_info("service_callback closing"); ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ return VCHIQ_ERROR; ++ } ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ } ++ ++ service->msg_queue[service->msg_insert & (MSG_QUEUE_SIZE - 1)] = ++ header; ++ ++ /* A write memory barrier is needed to ensure that the store of header ++ is completed before the insertion point is updated */ ++ vcos_wmb(&service->msg_queue[service->msg_insert & (MSG_QUEUE_SIZE - 1)]); ++ ++ service->msg_insert++; ++ vcos_event_signal(&service->insert_event); ++ ++ /* If there is a thread waiting in DEQUEUE_MESSAGE, or if ++ there is a MESSAGE_AVAILABLE in the completion queue then ++ bypass the completion queue. */ ++ if (((service->message_available_pos - instance->completion_remove) >= 0) || ++ service->dequeue_pending) ++ { ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ service->dequeue_pending = 0; ++ return VCHIQ_SUCCESS; ++ } ++ ++ header = NULL; ++ } ++ DEBUG_TRACE(SERVICE_CALLBACK_LINE); ++ ++ return add_completion(instance, reason, header, service, bulk_userdata); ++} ++ ++/**************************************************************************** ++* ++* vchiq_ioctl ++* ++***************************************************************************/ ++ ++static long ++vchiq_ioctl(struct file *file, unsigned int cmd, unsigned long arg) ++{ ++ VCHIQ_INSTANCE_T instance = file->private_data; ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ long ret = 0; ++ int i, rc; ++ DEBUG_INITIALISE(g_state.local) ++ ++ vcos_log_trace("vchiq_ioctl - instance %x, cmd %s, arg %lx", ++ (unsigned int)instance, ++ ((_IOC_TYPE(cmd) == VCHIQ_IOC_MAGIC) && (_IOC_NR(cmd) <= VCHIQ_IOC_MAX)) ? ++ ioctl_names[_IOC_NR(cmd)] : "<invalid>", arg); ++ ++ switch (cmd) { ++ case VCHIQ_IOC_SHUTDOWN: ++ if (!instance->connected) ++ break; ++ ++ /* Remove all services */ ++ for (i = 0; i < MAX_SERVICES; i++) { ++ USER_SERVICE_T *service = &instance->services[i]; ++ if (service->service != NULL) { ++ status = vchiq_remove_service(&service->service->base); ++ if (status != VCHIQ_SUCCESS) ++ break; ++ service->service = NULL; ++ } ++ } ++ ++ if (status == VCHIQ_SUCCESS) { ++ /* Wake the completion thread and ask it to exit */ ++ instance->closing = 1; ++ vcos_event_signal(&instance->insert_event); ++ } ++ ++ break; ++ ++ case VCHIQ_IOC_CONNECT: ++ if (instance->connected) { ++ ret = -EINVAL; ++ break; ++ } ++ if ((rc=vcos_mutex_lock(&instance->state->mutex)) != VCOS_SUCCESS) { ++ vcos_log_error("vchiq: connect: could not lock mutex for state %d: %d", ++ instance->state->id, rc); ++ ret = -EINTR; ++ break; ++ } ++ status = vchiq_connect_internal(instance->state, instance); ++ vcos_mutex_unlock(&instance->state->mutex); ++ ++ if (status == VCHIQ_SUCCESS) ++ instance->connected = 1; ++ else ++ vcos_log_error("vchiq: could not connect: %d", status); ++ break; ++ ++ case VCHIQ_IOC_CREATE_SERVICE: ++ { ++ VCHIQ_CREATE_SERVICE_T args; ++ VCHIQ_SERVICE_T *service = NULL; ++ USER_SERVICE_T *user_service = NULL; ++ void *userdata; ++ int srvstate; ++ ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ ++ for (i = 0; i < MAX_SERVICES; i++) { ++ if (instance->services[i].service == NULL) { ++ user_service = &instance->services[i]; ++ break; ++ } ++ } ++ ++ if (!user_service) { ++ ret = -EMFILE; ++ break; ++ } ++ ++ if (args.is_open) { ++ if (instance->connected) ++ srvstate = VCHIQ_SRVSTATE_OPENING; ++ else { ++ ret = -ENOTCONN; ++ break; ++ } ++ } else { ++ srvstate = ++ instance->connected ? ++ VCHIQ_SRVSTATE_LISTENING : ++ VCHIQ_SRVSTATE_HIDDEN; ++ } ++ ++ vcos_mutex_lock(&instance->state->mutex); ++ ++ userdata = args.params.userdata; ++ args.params.callback = service_callback; ++ args.params.userdata = user_service; ++ service = ++ vchiq_add_service_internal(instance->state, ++ &args.params, srvstate, ++ instance); ++ ++ vcos_mutex_unlock(&instance->state->mutex); ++ ++ if (service != NULL) { ++ user_service->service = service; ++ user_service->userdata = userdata; ++ user_service->instance = instance; ++ user_service->handle = i; ++ user_service->is_vchi = args.is_vchi; ++ user_service->dequeue_pending = 0; ++ user_service->message_available_pos = instance->completion_remove - 1; ++ user_service->msg_insert = 0; ++ user_service->msg_remove = 0; ++ vcos_event_create(&user_service->insert_event, "insert_event"); ++ vcos_event_create(&user_service->remove_event, "remove_event"); ++ ++ if (args.is_open) { ++ status = ++ vchiq_open_service_internal ++ (service, instance->pid); ++ if (status != VCHIQ_SUCCESS) { ++ vchiq_remove_service ++ (&service->base); ++ ret = ++ (status == ++ VCHIQ_RETRY) ? -EINTR : ++ -EIO; ++ user_service->service = NULL; ++ user_service->instance = NULL; ++ vcos_event_delete(&user_service->insert_event); ++ vcos_event_delete(&user_service->remove_event); ++ break; ++ } ++ } ++ ++ if (copy_to_user((void __user *) ++ &(((VCHIQ_CREATE_SERVICE_T __user ++ *) arg)->handle), ++ (const void *)&user_service-> ++ handle, ++ sizeof(user_service-> ++ handle)) != 0) ++ ret = -EFAULT; ++ } else { ++ ret = -EEXIST; ++ } ++ } ++ break; ++ ++ case VCHIQ_IOC_CLOSE_SERVICE: ++ { ++ USER_SERVICE_T *user_service; ++ int handle = (int)arg; ++ ++ user_service = find_service_by_handle(instance, handle); ++ if (user_service != NULL) ++ { ++ int is_server = (user_service->service->public_fourcc != VCHIQ_FOURCC_INVALID); ++ ++ status = ++ vchiq_close_service(&user_service->service->base); ++ if ((status == VCHIQ_SUCCESS) && !is_server) ++ { ++ vcos_event_delete(&user_service->insert_event); ++ vcos_event_delete(&user_service->remove_event); ++ user_service->service = NULL; ++ } ++ } else ++ ret = -EINVAL; ++ } ++ break; ++ ++ case VCHIQ_IOC_REMOVE_SERVICE: ++ { ++ USER_SERVICE_T *user_service; ++ int handle = (int)arg; ++ ++ user_service = find_service_by_handle(instance, handle); ++ if (user_service != NULL) ++ { ++ status = ++ vchiq_remove_service(&user_service->service->base); ++ if (status == VCHIQ_SUCCESS) ++ { ++ vcos_event_delete(&user_service->insert_event); ++ vcos_event_delete(&user_service->remove_event); ++ user_service->service = NULL; ++ } ++ } else ++ ret = -EINVAL; ++ } ++ break; ++ ++ case VCHIQ_IOC_USE_SERVICE: ++ case VCHIQ_IOC_RELEASE_SERVICE: ++ { ++ USER_SERVICE_T *user_service; ++ int handle = (int)arg; ++ ++ user_service = find_service_by_handle(instance, handle); ++ if (user_service != NULL) ++ { ++ status = (cmd == VCHIQ_IOC_USE_SERVICE) ? vchiq_use_service(&user_service->service->base) : vchiq_release_service(&user_service->service->base); ++ if (status != VCHIQ_SUCCESS) ++ { ++ ret = -EINVAL; // ??? ++ } ++ } ++ } ++ break; ++ ++ case VCHIQ_IOC_QUEUE_MESSAGE: ++ { ++ VCHIQ_QUEUE_MESSAGE_T args; ++ USER_SERVICE_T *user_service; ++ ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ user_service = find_service_by_handle(instance, args.handle); ++ if ((user_service != NULL) && (args.count <= MAX_ELEMENTS)) ++ { ++ /* Copy elements into kernel space */ ++ VCHIQ_ELEMENT_T elements[MAX_ELEMENTS]; ++ if (copy_from_user ++ (elements, args.elements, ++ args.count * sizeof(VCHIQ_ELEMENT_T)) == 0) ++ status = ++ vchiq_queue_message ++ (&user_service->service->base, ++ elements, args.count); ++ else ++ ret = -EFAULT; ++ } else { ++ ret = -EINVAL; ++ } ++ } ++ break; ++ ++ case VCHIQ_IOC_QUEUE_BULK_TRANSMIT: ++ case VCHIQ_IOC_QUEUE_BULK_RECEIVE: ++ { ++ VCHIQ_QUEUE_BULK_TRANSFER_T args; ++ USER_SERVICE_T *user_service; ++ VCHIQ_BULK_DIR_T dir = ++ (cmd == VCHIQ_IOC_QUEUE_BULK_TRANSMIT) ? ++ VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE; ++ ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ user_service = find_service_by_handle(instance, args.handle); ++ if (user_service != NULL) ++ { ++ status = ++ vchiq_bulk_transfer ++ ((VCHIQ_SERVICE_T *)user_service->service, ++ VCHI_MEM_HANDLE_INVALID, ++ args.data, args.size, ++ args.userdata, args.mode, ++ dir); ++ } else { ++ ret = -EINVAL; ++ } ++ } ++ break; ++ ++ case VCHIQ_IOC_AWAIT_COMPLETION: ++ { ++ VCHIQ_AWAIT_COMPLETION_T args; ++ ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ if (!instance->connected) { ++ ret = -ENOTCONN; ++ break; ++ } ++ ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ while ((instance->completion_remove == ++ instance->completion_insert) ++ && !instance->closing) { ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ if (vcos_event_wait(&instance->insert_event) != ++ VCOS_SUCCESS) { ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ vcos_log_info ++ ("AWAIT_COMPLETION interrupted"); ++ ret = -EINTR; ++ break; ++ } ++ } ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ ++ /* A read memory barrier is needed to stop prefetch of a stale ++ completion record */ ++ vcos_rmb(); ++ ++ if (ret == 0) { ++ int msgbufcount = args.msgbufcount; ++ for (ret = 0; ret < args.count; ret++) { ++ VCHIQ_COMPLETION_DATA_T *completion; ++ USER_SERVICE_T *service; ++ VCHIQ_HEADER_T *header; ++ if (instance->completion_remove == ++ instance->completion_insert) ++ break; ++ completion = ++ &instance-> ++ completions ++ [instance->completion_remove & ++ (MAX_COMPLETIONS - 1)]; ++ ++ service = (USER_SERVICE_T *)completion->service_userdata; ++ completion->service_userdata = service->userdata; ++ ++ header = completion->header; ++ if (header) ++ { ++ void __user *msgbuf; ++ int msglen; ++ ++ msglen = header->size + sizeof(VCHIQ_HEADER_T); ++ /* This must be a VCHIQ-style service */ ++ if (args.msgbufsize < msglen) ++ { ++ vcos_log_error("header %x: msgbufsize %x < msglen %x", ++ (unsigned int)header, args.msgbufsize, msglen); ++ vcos_assert(0); ++ if (ret == 0) ++ ret = -EMSGSIZE; ++ break; ++ } ++ if (msgbufcount <= 0) ++ { ++ /* Stall here for lack of a buffer for the message */ ++ break; ++ } ++ /* Get the pointer from user space */ ++ msgbufcount--; ++ if (copy_from_user(&msgbuf, ++ (const void __user *)&args.msgbufs[msgbufcount], ++ sizeof(msgbuf)) != 0) ++ { ++ if (ret == 0) ++ ret = -EFAULT; ++ break; ++ } ++ ++ /* Copy the message to user space */ ++ if (copy_to_user(msgbuf, header, msglen) != 0) ++ { ++ if (ret == 0) ++ ret = -EFAULT; ++ break; ++ } ++ ++ /* Now it has been copied, the message can be released. */ ++ vchiq_release_message(&service->service->base, header); ++ ++ /* The completion must point to the msgbuf */ ++ completion->header = msgbuf; ++ } ++ ++ if (copy_to_user ++ ((void __user *)((size_t) args.buf + ++ ret * ++ sizeof ++ (VCHIQ_COMPLETION_DATA_T)), ++ completion, ++ sizeof(VCHIQ_COMPLETION_DATA_T)) != ++ 0) { ++ if (ret == 0) ++ ret = -EFAULT; ++ break; ++ } ++ instance->completion_remove++; ++ } ++ ++ if (msgbufcount != args.msgbufcount) ++ { ++ if (copy_to_user((void __user *) ++ &((VCHIQ_AWAIT_COMPLETION_T *)arg)->msgbufcount, ++ &msgbufcount, sizeof(msgbufcount)) != 0) ++ { ++ ret = -EFAULT; ++ break; ++ } ++ } ++ } ++ ++ if (ret != 0) ++ vcos_event_signal(&instance->remove_event); ++ DEBUG_TRACE(AWAIT_COMPLETION_LINE); ++ } ++ break; ++ ++ case VCHIQ_IOC_DEQUEUE_MESSAGE: ++ { ++ VCHIQ_DEQUEUE_MESSAGE_T args; ++ USER_SERVICE_T *user_service; ++ VCHIQ_HEADER_T *header; ++ ++ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ user_service = &instance->services[args.handle]; ++ if ((args.handle < 0) || (args.handle >= MAX_SERVICES) || ++ (user_service->service == NULL) || ++ (user_service->is_vchi == 0)) { ++ ret = -EINVAL; ++ break; ++ } ++ if (user_service->msg_remove == user_service->msg_insert) ++ { ++ if (!args.blocking) ++ { ++ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); ++ ret = -EWOULDBLOCK; ++ break; ++ } ++ user_service->dequeue_pending = 1; ++ do { ++ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); ++ if (vcos_event_wait(&user_service->insert_event) != ++ VCOS_SUCCESS) { ++ vcos_log_info("DEQUEUE_MESSAGE interrupted"); ++ ret = -EINTR; ++ break; ++ } ++ } ++ while (user_service->msg_remove == user_service->msg_insert); ++ } ++ ++ /* A read memory barrier is needed to stop prefetch of a stale ++ header value */ ++ vcos_rmb(); ++ ++ header = user_service->msg_queue[user_service->msg_remove & ++ (MSG_QUEUE_SIZE - 1)]; ++ if (header == NULL) ++ ret = -ENOTCONN; ++ else if (header->size <= args.bufsize) ++ { ++ /* Copy to user space if msgbuf is not NULL */ ++ if ((args.buf == NULL) || ++ (copy_to_user((void __user *)args.buf, header->data, ++ header->size) == 0)) ++ { ++ ret = header->size; ++ vchiq_release_message(&user_service->service->base, ++ header); ++ user_service->msg_remove++; ++ vcos_event_signal(&user_service->remove_event); ++ } ++ else ++ ret = -EFAULT; ++ } ++ else ++ { ++ vcos_log_error("header %x: bufsize %x < size %x", ++ (unsigned int)header, args.bufsize, header->size); ++ vcos_assert(0); ++ ret = -EMSGSIZE; ++ } ++ DEBUG_TRACE(DEQUEUE_MESSAGE_LINE); ++ } ++ break; ++ ++ case VCHIQ_IOC_GET_CLIENT_ID: ++ { ++ USER_SERVICE_T *user_service; ++ int handle = (int)arg; ++ ++ user_service = find_service_by_handle(instance, handle); ++ if (user_service != NULL) ++ ret = vchiq_get_client_id(&user_service->service->base); ++ else ++ ret = 0; ++ } ++ break; ++ ++ case VCHIQ_IOC_GET_CONFIG: ++ { ++ VCHIQ_GET_CONFIG_T args; ++ VCHIQ_CONFIG_T config; ++ ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ if (args.config_size > sizeof(config)) ++ { ++ ret = -EINVAL; ++ break; ++ } ++ status = vchiq_get_config(instance, args.config_size, &config); ++ if (status == VCHIQ_SUCCESS) ++ { ++ if (copy_to_user((void __user *)args.pconfig, ++ &config, args.config_size) != 0) ++ { ++ ret = -EFAULT; ++ break; ++ } ++ } ++ } ++ break; ++ ++ case VCHIQ_IOC_SET_SERVICE_OPTION: ++ { ++ VCHIQ_SET_SERVICE_OPTION_T args; ++ USER_SERVICE_T *user_service; ++ ++ if (copy_from_user( ++ &args, (const void __user *)arg, ++ sizeof(args)) != 0) ++ { ++ ret = -EFAULT; ++ break; ++ } ++ ++ user_service = find_service_by_handle(instance, args.handle); ++ if (user_service != NULL) ++ { ++ status = vchiq_set_service_option( ++ &user_service->service->base, ++ args.option, args.value); ++ } ++ else ++ { ++ ret = -EINVAL; ++ } ++ } ++ break; ++ ++ default: ++ ret = -ENOTTY; ++ break; ++ } ++ ++ if (ret == 0) { ++ if (status == VCHIQ_ERROR) ++ ret = -EIO; ++ else if (status == VCHIQ_RETRY) ++ ret = -EINTR; ++ } ++ ++ if ((ret < 0) && (ret != -EINTR) && (ret != -EWOULDBLOCK)) ++ vcos_log_warn(" ioctl instance %lx, cmd %s -> status %d, %ld", ++ (unsigned long)instance, ++ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? ioctl_names[_IOC_NR(cmd)] : ++ "<invalid>", status, ret); ++ else ++ vcos_log_trace(" ioctl instance %lx, cmd %s -> status %d, %ld", ++ (unsigned long)instance, ++ (_IOC_NR(cmd) <= VCHIQ_IOC_MAX) ? ioctl_names[_IOC_NR(cmd)] : ++ "<invalid>", status, ret); ++ ++ return ret; ++} ++ ++/**************************************************************************** ++* ++* vchiq_open ++* ++***************************************************************************/ ++ ++static int ++vchiq_open(struct inode *inode, struct file *file) ++{ ++ int dev = iminor(inode) & 0x0f; ++ vcos_log_info("vchiq_open"); ++ switch (dev) { ++ case VCHIQ_MINOR: ++ { ++ VCHIQ_STATE_T *state = vchiq_get_state(); ++ VCHIQ_INSTANCE_T instance; ++ ++ if (!state) ++ { ++ vcos_log_error( "vchiq has no connection to VideoCore"); ++ return -ENOTCONN; ++ } ++ ++ instance = kzalloc(sizeof(*instance), GFP_KERNEL); ++ if (!instance) ++ return -ENOMEM; ++ ++ instance->state = state; ++ instance->pid = current->tgid; ++ vcos_event_create(&instance->insert_event, DEVICE_NAME); ++ vcos_event_create(&instance->remove_event, DEVICE_NAME); ++ ++ file->private_data = instance; ++ } ++ break; ++ ++ default: ++ vcos_log_error("Unknown minor device: %d", dev); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* vchiq_release ++* ++***************************************************************************/ ++ ++static int ++vchiq_release(struct inode *inode, struct file *file) ++{ ++ int dev = iminor(inode) & 0x0f; ++ int ret = 0; ++ switch (dev) { ++ case VCHIQ_MINOR: ++ { ++ VCHIQ_INSTANCE_T instance = file->private_data; ++ int i; ++ ++ vcos_log_info("vchiq_release: instance=%lx", ++ (unsigned long)instance); ++ ++ instance->closing = 1; ++ ++ /* Wake the slot handler if the completion queue is full */ ++ vcos_event_signal(&instance->remove_event); ++ ++ /* Mark all services for termination... */ ++ ++ for (i = 0; i < MAX_SERVICES; i++) { ++ USER_SERVICE_T *user_service = ++ &instance->services[i]; ++ if (user_service->service != NULL) ++ { ++ /* Wake the slot handler if the msg queue is full */ ++ vcos_event_signal(&user_service->remove_event); ++ ++ if ((user_service->service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) && ++ (user_service->service->srvstate != VCHIQ_SRVSTATE_LISTENING)) ++ { ++ vchiq_terminate_service_internal(user_service->service); ++ } ++ } ++ } ++ ++ /* ...and wait for them to die */ ++ ++ for (i = 0; i < MAX_SERVICES; i++) { ++ USER_SERVICE_T *user_service = ++ &instance->services[i]; ++ if (user_service->service != NULL) ++ { ++ /* Wait in this non-portable fashion because interruptible ++ calls will not block in this context. */ ++ while ((user_service->service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) && ++ (user_service->service->srvstate != VCHIQ_SRVSTATE_LISTENING)) ++ { ++ down(&user_service->service->remove_event); ++ } ++ ++ vchiq_free_service_internal ++ (user_service->service); ++ } ++ } ++ ++ vcos_event_delete(&instance->insert_event); ++ vcos_event_delete(&instance->remove_event); ++ ++ kfree(instance); ++ file->private_data = NULL; ++ } ++ break; ++ ++ default: ++ vcos_log_error("Unknown minor device: %d", dev); ++ ret = -ENXIO; ++ } ++ ++ return ret; ++} ++ ++/**************************************************************************** ++* ++* vchiq_dump ++* ++***************************************************************************/ ++ ++void ++vchiq_dump(void *dump_context, const char *str, int len) ++{ ++ DUMP_CONTEXT_T *context = (DUMP_CONTEXT_T *)dump_context; ++ ++ if ((context->actual >= 0) && (context->actual < context->space)) ++ { ++ int copy_bytes; ++ if (context->offset > 0) ++ { ++ int skip_bytes = vcos_min(len, context->offset); ++ str += skip_bytes; ++ len -= skip_bytes; ++ context->offset -= skip_bytes; ++ if (context->offset > 0) ++ return; ++ } ++ copy_bytes = vcos_min(len, context->space - context->actual); ++ if (copy_bytes == 0) ++ return; ++ if (copy_to_user(context->buf + context->actual, str, copy_bytes)) ++ context->actual = -EFAULT; ++ context->actual += copy_bytes; ++ len -= copy_bytes; ++ ++ /* If tne terminating NUL is included in the length, then it marks ++ * the end of a line and should be replaced with a carriage return. ++ */ ++ if ((len == 0) && (str[copy_bytes - 1] == '\0')) ++ { ++ char cr = '\n'; ++ if (copy_to_user(context->buf + context->actual - 1, &cr, 1)) ++ { ++ context->actual = -EFAULT; ++ } ++ } ++ } ++} ++ ++/**************************************************************************** ++* ++* vchiq_dump_platform_instance_state ++* ++***************************************************************************/ ++ ++void ++vchiq_dump_platform_instances(void *dump_context) ++{ ++ VCHIQ_STATE_T *state = vchiq_get_state(); ++ char buf[80]; ++ int len; ++ int i; ++ ++ /* There is no list of instances, so instead scan all services, ++ marking those that have been dumped. */ ++ ++ for (i = 0; i < state->unused_service; i++) ++ { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ VCHIQ_INSTANCE_T instance; ++ ++ if (service ++ && ((instance = service->instance) != NULL) ++ && (service->base.callback == service_callback)) ++ instance->mark = 0; ++ } ++ ++ for (i = 0; i < state->unused_service; i++) ++ { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ VCHIQ_INSTANCE_T instance; ++ ++ if (service ++ && ((instance = service->instance) != NULL) ++ && (service->base.callback == service_callback)) ++ { ++ if (!instance->mark) ++ { ++ len = vcos_snprintf(buf, sizeof(buf), ++ "Instance %x: pid %d,%s completions %d/%d", ++ (unsigned int)instance, instance->pid, ++ instance->connected ? " connected," : "", ++ instance->completion_insert - instance->completion_remove, ++ MAX_COMPLETIONS); ++ ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ instance->mark = 1; ++ } ++ } ++ } ++} ++ ++/**************************************************************************** ++* ++* vchiq_dump_platform_service_state ++* ++***************************************************************************/ ++ ++void ++vchiq_dump_platform_service_state(void *dump_context, VCHIQ_SERVICE_T *service) ++{ ++ USER_SERVICE_T *user_service = (USER_SERVICE_T *)service->base.userdata; ++ char buf[80]; ++ int len; ++ ++ len = vcos_snprintf(buf, sizeof(buf), " instance %x", ++ service->instance); ++ ++ if ((service->base.callback == service_callback) && user_service->is_vchi) ++ { ++ len += vcos_snprintf(buf + len, sizeof(buf) - len, ++ ", %d/%d messages", ++ user_service->msg_insert - user_service->msg_remove, ++ MSG_QUEUE_SIZE); ++ ++ if (user_service->dequeue_pending) ++ len += vcos_snprintf(buf + len, sizeof(buf) - len, ++ " (dequeue pending)"); ++ } ++ ++ vchiq_dump(dump_context, buf, len + 1); ++} ++ ++/**************************************************************************** ++* ++* vchiq_read ++* ++***************************************************************************/ ++ ++static ssize_t ++vchiq_read(struct file * file, char __user * buf, ++ size_t count, loff_t *ppos) ++{ ++ DUMP_CONTEXT_T context; ++ context.buf = buf; ++ context.actual = 0; ++ context.space = count; ++ context.offset = *ppos; ++ ++ vchiq_dump_state(&context, &g_state); ++ ++ if (context.actual >= 0) ++ *ppos += context.actual; ++ ++ return context.actual; ++} ++ ++VCHIQ_STATE_T * ++vchiq_get_state(void) ++{ ++ ++ if (g_state.remote == NULL) ++ { ++ printk( "%s: g_state.remote == NULL\n", __func__ ); ++ } ++ else ++ { ++ if ( g_state.remote->initialised != 1) ++ { ++ printk( "%s: g_state.remote->initialised != 1 (%d)\n", __func__, g_state.remote->initialised ); ++ } ++ } ++ ++ return ((g_state.remote != NULL) && ++ (g_state.remote->initialised == 1)) ? &g_state : NULL; ++} ++ ++static const struct file_operations ++vchiq_fops = { ++ .owner = THIS_MODULE, ++ .unlocked_ioctl = vchiq_ioctl, ++ .open = vchiq_open, ++ .release = vchiq_release, ++ .read = vchiq_read ++}; ++ ++/**************************************************************************** ++* ++* vchiq_init - called when the module is loaded. ++* ++***************************************************************************/ ++ ++static int __init ++vchiq_init(void) ++{ ++ int err; ++ void *ptr_err; ++ ++ err = vchiq_platform_vcos_init(); ++ if (err != 0) ++ goto failed_platform_vcos_init; ++ ++ vcos_log_set_level(VCOS_LOG_CATEGORY, vchiq_default_arm_log_level); ++ vcos_log_register("vchiq_arm", VCOS_LOG_CATEGORY); ++ ++ if ((err = ++ alloc_chrdev_region(&vchiq_devid, VCHIQ_MINOR, 1, ++ DEVICE_NAME)) != 0) { ++ vcos_log_error("Unable to allocate device number"); ++ goto failed_alloc_chrdev; ++ } ++ cdev_init(&vchiq_cdev, &vchiq_fops); ++ vchiq_cdev.owner = THIS_MODULE; ++ if ((err = cdev_add(&vchiq_cdev, vchiq_devid, 1)) != 0) { ++ vcos_log_error("Unable to register device"); ++ goto failed_cdev_add; ++ } ++ ++ /* create sysfs entries */ ++ vchiq_class = class_create(THIS_MODULE, DEVICE_NAME); ++ if (IS_ERR(ptr_err = vchiq_class)) ++ goto failed_class_create; ++ ++ vchiq_dev = device_create(vchiq_class, NULL, ++ vchiq_devid, NULL, "vchiq"); ++ if (IS_ERR(ptr_err = vchiq_dev)) ++ goto failed_device_create; ++ ++ err = vchiq_platform_init(&g_state); ++ if (err != 0) ++ goto failed_platform_init; ++ ++ vcos_log_error("vchiq: initialised - version %d (min %d), device %d.%d", ++ VCHIQ_VERSION, VCHIQ_VERSION_MIN, ++ MAJOR(vchiq_devid), MINOR(vchiq_devid)); ++ ++ return 0; ++ ++failed_platform_init: ++ device_destroy(vchiq_class, vchiq_devid); ++failed_device_create: ++ class_destroy(vchiq_class); ++failed_class_create: ++ cdev_del(&vchiq_cdev); ++ err = PTR_ERR(ptr_err); ++failed_cdev_add: ++ unregister_chrdev_region(vchiq_devid, 1); ++failed_alloc_chrdev: ++failed_platform_vcos_init: ++ printk(KERN_WARNING "could not load vchiq\n"); ++ return err; ++} ++/**************************************************************************** ++* ++* vchiq_exit - called when the module is unloaded. ++* ++***************************************************************************/ ++ ++static void __exit ++vchiq_exit(void) ++{ ++ vchiq_platform_exit(&g_state); ++ device_destroy(vchiq_class, vchiq_devid); ++ class_destroy(vchiq_class); ++ cdev_del(&vchiq_cdev); ++ unregister_chrdev_region(vchiq_devid, 1); ++ vcos_log_unregister(VCOS_LOG_CATEGORY); ++} ++ ++module_init(vchiq_init); ++module_exit(vchiq_exit); ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Broadcom Corporation"); +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h +@@ -0,0 +1,38 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef VCHIQ_ARM_H ++#define VCHIQ_ARM_H ++ ++#include "vchiq_core.h" ++ ++extern VCOS_LOG_CAT_T vchiq_arm_log_category; ++ ++extern int __init ++vchiq_platform_vcos_init(void); ++ ++extern int __init ++vchiq_platform_init(VCHIQ_STATE_T *state); ++ ++extern void __exit ++vchiq_platform_exit(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATE_T * ++vchiq_get_state(void); ++ ++#endif /* VCHIQ_ARM_H */ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_cfg.h +@@ -0,0 +1,43 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef VCHIQ_CFG_H ++#define VCHIQ_CFG_H ++ ++#define VCHIQ_MAGIC VCHIQ_MAKE_FOURCC('V','C','H','I') ++/* The version of VCHIQ - change with any non-trivial change */ ++#define VCHIQ_VERSION 2 ++/* The minimum compatible version - update to match VCHIQ_VERSION with any incompatible change */ ++#define VCHIQ_VERSION_MIN 2 ++ ++#define VCHIQ_MAX_SERVICES 4096 ++#define VCHIQ_MAX_SLOTS 128 ++#define VCHIQ_MAX_SLOTS_PER_SIDE 64 ++ ++#define VCHIQ_NUM_CURRENT_BULKS 32 ++#define VCHIQ_NUM_SERVICE_BULKS 4 ++ ++#ifndef VCHIQ_ENABLE_DEBUG ++#define VCHIQ_ENABLE_DEBUG 1 ++#endif ++ ++#ifndef VCHIQ_ENABLE_STATS ++#define VCHIQ_ENABLE_STATS 1 ++#endif ++ ++#endif /* VCHIQ_CFG_H */ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.c +@@ -0,0 +1,101 @@ ++/***************************************************************************** ++* Copyright 2001 - 2010 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include "vcos.h" ++#include "vchiq_connected.h" ++#include <linux/module.h> ++ ++#define MAX_CALLBACKS 10 ++ ++static int g_connected = 0; ++static int g_num_deferred_callbacks; ++static VCHIQ_CONNECTED_CALLBACK_T g_deferred_callback[ MAX_CALLBACKS ]; ++static VCOS_ONCE_T g_once_init; ++static VCOS_MUTEX_T g_connected_mutex; ++ ++extern VCOS_LOG_CAT_T vchiq_core_log_category; ++#define VCOS_LOG_CATEGORY (&vchiq_core_log_category) ++ ++/**************************************************************************** ++* ++* Function to initialize our lock. ++* ++***************************************************************************/ ++ ++static void connected_init( void ) ++{ ++ vcos_mutex_create( &g_connected_mutex, "connected_mutex"); ++} ++ ++/**************************************************************************** ++* ++* This function is used to defer initialization until the vchiq stack is ++* initialized. If the stack is already initialized, then the callback will ++* be made immediately, otherwise it will be deferred until ++* vchiq_call_connected_callbacks is called. ++* ++***************************************************************************/ ++ ++void vchiq_add_connected_callback( VCHIQ_CONNECTED_CALLBACK_T callback ) ++{ ++ vcos_once( &g_once_init, connected_init ); ++ ++ vcos_mutex_lock( &g_connected_mutex ); ++ ++ if ( g_connected ) ++ { ++ // We're already connected. Call the callback immediately. ++ ++ callback(); ++ } ++ else ++ { ++ if ( g_num_deferred_callbacks >= MAX_CALLBACKS ) ++ { ++ vcos_log_error( "There already %d callback registered - please increase MAX_CALLBACKS", ++ g_num_deferred_callbacks ); ++ } ++ else ++ { ++ g_deferred_callback[ g_num_deferred_callbacks ] = callback; ++ g_num_deferred_callbacks++; ++ } ++ } ++ vcos_mutex_unlock( &g_connected_mutex ); ++} ++ ++/**************************************************************************** ++* ++* This function is called by the vchiq stack once it has been connected to ++* the videocore and clients can start to use the stack. ++* ++***************************************************************************/ ++ ++void vchiq_call_connected_callbacks( void ) ++{ ++ int i; ++ ++ vcos_once( &g_once_init, connected_init ); ++ ++ vcos_mutex_lock( &g_connected_mutex ); ++ for ( i = 0; i < g_num_deferred_callbacks; i++ )\ ++ { ++ g_deferred_callback[i](); ++ } ++ g_num_deferred_callbacks = 0; ++ g_connected = 1; ++ vcos_mutex_unlock( &g_connected_mutex ); ++} ++ ++EXPORT_SYMBOL( vchiq_add_connected_callback ); +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_connected.h +@@ -0,0 +1,32 @@ ++/***************************************************************************** ++* Copyright 2001 - 2010 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#ifndef VCHIQ_CONNECTED_H ++#define VCHIQ_CONNECTED_H ++ ++/* ---- Include Files ----------------------------------------------------- */ ++ ++/* ---- Constants and Types ---------------------------------------------- */ ++ ++typedef void (*VCHIQ_CONNECTED_CALLBACK_T)( void ); ++ ++/* ---- Variable Externs ------------------------------------------------- */ ++ ++/* ---- Function Prototypes ---------------------------------------------- */ ++ ++void vchiq_add_connected_callback( VCHIQ_CONNECTED_CALLBACK_T callback ); ++void vchiq_call_connected_callbacks( void ); ++ ++#endif /* VCHIQ_CONNECTED_H */ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c +@@ -0,0 +1,2604 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "vchiq_core.h" ++ ++#define VCHIQ_SLOT_HANDLER_STACK 8192 ++ ++#define SLOT_INFO_FROM_INDEX(state, index) (state->slot_info + (index)) ++#define SLOT_DATA_FROM_INDEX(state, index) (state->slot_data + (index)) ++#define SLOT_INDEX_FROM_DATA(state, data) (((unsigned int)((char *)data - (char *)state->slot_data)) / VCHIQ_SLOT_SIZE) ++#define SLOT_INDEX_FROM_INFO(state, info) ((unsigned int)(info - state->slot_info)) ++#define SLOT_QUEUE_INDEX_FROM_POS(pos) ((int)((unsigned int)(pos) / VCHIQ_SLOT_SIZE)) ++ ++#define VCOS_LOG_CATEGORY (&vchiq_core_log_category) ++ ++#define BULK_INDEX(x) (x & (VCHIQ_NUM_SERVICE_BULKS - 1)) ++ ++typedef struct bulk_waiter_struct ++{ ++ VCOS_EVENT_T event; ++ int actual; ++} BULK_WAITER_T; ++ ++typedef struct vchiq_open_payload_struct{ ++ int fourcc; ++ int client_id; ++ short version; ++ short version_min; ++} VCHIQ_OPEN_PAYLOAD_T; ++ ++vcos_static_assert(sizeof(VCHIQ_HEADER_T) == 8); /* we require this for consistency between endpoints */ ++vcos_static_assert(IS_POW2(sizeof(VCHIQ_HEADER_T))); ++vcos_static_assert(IS_POW2(VCHIQ_NUM_CURRENT_BULKS)); ++vcos_static_assert(IS_POW2(VCHIQ_NUM_SERVICE_BULKS)); ++ ++VCOS_LOG_CAT_T vchiq_core_log_category; ++VCOS_LOG_CAT_T vchiq_core_msg_log_category; ++VCOS_LOG_LEVEL_T vchiq_default_core_log_level = VCOS_LOG_WARN; ++VCOS_LOG_LEVEL_T vchiq_default_core_msg_log_level = VCOS_LOG_WARN; ++ ++static const char *const srvstate_names[] = ++{ ++ "FREE", ++ "HIDDEN", ++ "LISTENING", ++ "OPENING", ++ "OPEN", ++ "CLOSESENT", ++ "CLOSING", ++ "CLOSEWAIT" ++}; ++ ++static const char *const reason_names[] = ++{ ++ "SERVICE_OPENED", ++ "SERVICE_CLOSED", ++ "MESSAGE_AVAILABLE", ++ "BULK_TRANSMIT_DONE", ++ "BULK_RECEIVE_DONE", ++ "BULK_TRANSMIT_ABORTED", ++ "BULK_RECEIVE_ABORTED" ++}; ++ ++static const char *const conn_state_names[] = ++{ ++ "DISCONNECTED", ++ "CONNECTED", ++ "PAUSING", ++ "PAUSE_SENT", ++ "PAUSED", ++ "RESUMING" ++}; ++ ++static const char *msg_type_str( unsigned int msg_type ) ++{ ++ switch (msg_type) { ++ case VCHIQ_MSG_PADDING: return "PADDING"; ++ case VCHIQ_MSG_CONNECT: return "CONNECT"; ++ case VCHIQ_MSG_OPEN: return "OPEN"; ++ case VCHIQ_MSG_OPENACK: return "OPENACK"; ++ case VCHIQ_MSG_CLOSE: return "CLOSE"; ++ case VCHIQ_MSG_DATA: return "DATA"; ++ case VCHIQ_MSG_BULK_RX: return "BULK_RX"; ++ case VCHIQ_MSG_BULK_TX: return "BULK_TX"; ++ case VCHIQ_MSG_BULK_RX_DONE: return "BULK_RX_DONE"; ++ case VCHIQ_MSG_BULK_TX_DONE: return "BULK_TX_DONE"; ++ case VCHIQ_MSG_PAUSE: return "PAUSE"; ++ case VCHIQ_MSG_RESUME: return "RESUME"; ++ } ++ return "???"; ++} ++ ++static inline void ++vchiq_set_service_state(VCHIQ_SERVICE_T *service, int newstate) ++{ ++ vcos_log_info("%d: srv:%d %s->%s", service->state->id, service->localport, ++ srvstate_names[service->srvstate], ++ srvstate_names[newstate]); ++ service->srvstate = newstate; ++} ++ ++static inline VCHIQ_STATUS_T ++make_service_callback(VCHIQ_SERVICE_T *service, VCHIQ_REASON_T reason, ++ VCHIQ_HEADER_T *header, void *bulk_userdata) ++{ ++ vcos_log_trace("%d: callback:%d (%s, %x, %x)", service->state->id, ++ service->localport, reason_names[reason], ++ (unsigned int)header, (unsigned int)bulk_userdata); ++ return service->base.callback(reason, header, &service->base, bulk_userdata); ++} ++ ++static inline void ++vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate) ++{ ++ vcos_log_info("%d: %s->%s", state->id, ++ conn_state_names[state->conn_state], ++ conn_state_names[newstate]); ++ state->conn_state = newstate; ++} ++ ++static inline void ++remote_event_create(REMOTE_EVENT_T *event) ++{ ++ event->armed = 0; ++ /* Don't clear the 'fired' flag because it may already have been set by the other side */ ++ vcos_event_create(event->event, "vchiq"); ++} ++ ++static inline void ++remote_event_destroy(REMOTE_EVENT_T *event) ++{ ++ vcos_event_delete(event->event); ++} ++ ++static inline int ++remote_event_wait(REMOTE_EVENT_T *event) ++{ ++ if (!event->fired) ++ { ++ event->armed = 1; ++ if (event->fired) /* Also ensures the write has completed */ ++ event->armed = 0; ++ else if (vcos_event_wait(event->event) != VCOS_SUCCESS) ++ return 0; ++ } ++ ++ event->fired = 0; ++ return 1; ++} ++ ++static inline void ++remote_event_signal_local(REMOTE_EVENT_T *event) ++{ ++ event->armed = 0; ++ vcos_event_signal(event->event); ++} ++ ++static inline void ++remote_event_poll(REMOTE_EVENT_T *event) ++{ ++ if (event->armed) ++ remote_event_signal_local(event); ++} ++ ++void ++remote_event_pollall(VCHIQ_STATE_T *state) ++{ ++ remote_event_poll(&state->local->trigger); ++ remote_event_poll(&state->local->recycle); ++} ++ ++/* Round up message sizes so that any space at the end of a slot is always big ++ enough for a header. This relies on header size being a power of two, which ++ has been verified earlier by a static assertion. */ ++ ++static inline unsigned int ++calc_stride(unsigned int size) ++{ ++ /* Allow room for the header */ ++ size += sizeof(VCHIQ_HEADER_T); ++ ++ /* Round up */ ++ return (size + sizeof(VCHIQ_HEADER_T) - 1) & ~(sizeof(VCHIQ_HEADER_T) - 1); ++} ++ ++static VCHIQ_SERVICE_T * ++get_listening_service(VCHIQ_STATE_T *state, int fourcc) ++{ ++ int i; ++ ++ vcos_assert(fourcc != VCHIQ_FOURCC_INVALID); ++ ++ for (i = 0; i < state->unused_service; i++) ++ { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ if (service && ++ (service->public_fourcc == fourcc) && ++ ((service->srvstate == VCHIQ_SRVSTATE_LISTENING) || ++ ((service->srvstate == VCHIQ_SRVSTATE_OPEN) && ++ (service->remoteport == VCHIQ_PORT_FREE)))) ++ return service; ++ } ++ ++ return NULL; ++} ++ ++static VCHIQ_SERVICE_T * ++get_connected_service(VCHIQ_STATE_T *state, unsigned int port) ++{ ++ int i; ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ if (service && (service->srvstate == VCHIQ_SRVSTATE_OPEN) ++ && (service->remoteport == port)) { ++ return service; ++ } ++ } ++ return NULL; ++} ++ ++static inline void ++request_poll(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int poll_type) ++{ ++ if (service) ++ { ++ vcos_atomic_flags_or(&service->poll_flags, (1 << poll_type)); ++ vcos_atomic_flags_or(&state->poll_services[service->localport>>5], ++ (1 <<(service->localport & 0x1f))); ++ } ++ ++ state->poll_needed = 1; ++ vcos_wmb(&state->poll_needed); ++ ++ /* ... and ensure the slot handler runs. */ ++ remote_event_signal_local(&state->local->trigger); ++} ++ ++/* Called from queue_message, by the slot handler and application threads, ++ with slot_mutex held */ ++static VCHIQ_HEADER_T * ++reserve_space(VCHIQ_STATE_T *state, int space, int is_blocking) ++{ ++ VCHIQ_SHARED_STATE_T *local = state->local; ++ int tx_pos = state->local_tx_pos; ++ int slot_space = VCHIQ_SLOT_SIZE - (tx_pos & VCHIQ_SLOT_MASK); ++ ++ if (space > slot_space) { ++ VCHIQ_HEADER_T *header; ++ /* Fill the remaining space with padding */ ++ vcos_assert(state->tx_data != NULL); ++ header = (VCHIQ_HEADER_T *) (state->tx_data + (tx_pos & VCHIQ_SLOT_MASK)); ++ header->msgid = VCHIQ_MSGID_PADDING; ++ header->size = slot_space - sizeof(VCHIQ_HEADER_T); ++ ++ tx_pos += slot_space; ++ } ++ ++ /* If necessary, get the next slot. */ ++ if ((tx_pos & VCHIQ_SLOT_MASK) == 0) ++ { ++ int slot_index; ++ ++ /* If there is no free slot... */ ++ if (tx_pos == (state->slot_queue_available * VCHIQ_SLOT_SIZE)) ++ { ++ /* ...wait for one. */ ++ VCHIQ_STATS_INC(state, slot_stalls); ++ ++ /* But first, flush through the last slot. */ ++ local->tx_pos = tx_pos; ++ remote_event_signal(&state->remote->trigger); ++ ++ do { ++ if (!is_blocking || ++ (vcos_event_wait(&state->slot_available_event) != VCOS_SUCCESS)) ++ { ++ return NULL; /* No space available now */ ++ } ++ } ++ while (tx_pos == (state->slot_queue_available * VCHIQ_SLOT_SIZE)); ++ } ++ ++ slot_index = local->slot_queue[SLOT_QUEUE_INDEX_FROM_POS(tx_pos) & VCHIQ_SLOT_QUEUE_MASK]; ++ state->tx_data = (char *)SLOT_DATA_FROM_INDEX(state, slot_index); ++ } ++ ++ state->local_tx_pos = tx_pos + space; ++ ++ return (VCHIQ_HEADER_T *)(state->tx_data + (tx_pos & VCHIQ_SLOT_MASK)); ++} ++ ++/* Called with slot_mutex held */ ++static void ++process_free_queue(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_SHARED_STATE_T *local = state->local; ++ BITSET_T service_found[BITSET_SIZE(VCHIQ_MAX_SERVICES)]; ++ int slot_queue_available; ++ ++ /* Use a read memory barrier to ensure that any state that may have ++ been modified by another thread is not masked by stale prefetched ++ values. */ ++ vcos_rmb(); ++ ++ /* Find slots which have been freed by the other side, and return them to ++ the available queue. */ ++ slot_queue_available = state->slot_queue_available; ++ ++ while (slot_queue_available != local->slot_queue_recycle) ++ { ++ int pos; ++ int slot_index = local->slot_queue[slot_queue_available++ & VCHIQ_SLOT_QUEUE_MASK]; ++ char *data = (char *)SLOT_DATA_FROM_INDEX(state, slot_index); ++ ++ vcos_log_trace("%d: pfq %d=%x %x %x", state->id, slot_index, ++ (unsigned int)data, local->slot_queue_recycle, ++ slot_queue_available); ++ ++ /* Initialise the bitmask for services which have used this slot */ ++ BITSET_ZERO(service_found); ++ ++ pos = 0; ++ ++ while (pos < VCHIQ_SLOT_SIZE) ++ { ++ VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)(data + pos); ++ int msgid = header->msgid; ++ if (VCHIQ_MSG_TYPE(msgid) == VCHIQ_MSG_DATA) ++ { ++ int port = VCHIQ_MSG_SRCPORT(msgid); ++ if (!BITSET_IS_SET(service_found, port)) ++ { ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &state->service_quotas[port]; ++ ++ /* Set the found bit for this service */ ++ BITSET_SET(service_found, port); ++ ++ if (service_quota->slot_use_count > 0) ++ { ++ service_quota->slot_use_count--; ++ /* Signal the service in case it has dropped below its quota */ ++ vcos_event_signal(&service_quota->quota_event); ++ vcos_log_trace("%d: pfq:%d %x@%x - slot_use->%d", ++ state->id, port, ++ header->size, (unsigned int)header, ++ service_quota->slot_use_count); ++ } ++ else ++ { ++ vcos_log_error("service %d slot_use_count=%d (header %x," ++ " msgid %x, header->msgid %x, header->size %x)", ++ port, service_quota->slot_use_count, ++ (unsigned int)header, msgid, header->msgid, ++ header->size); ++ vcos_assert(0); ++ } ++ } ++ } ++ ++ pos += calc_stride(header->size); ++ if (pos > VCHIQ_SLOT_SIZE) ++ { ++ vcos_log_error("pos %x: header %x, msgid %x, header->msgid %x, header->size %x", ++ pos, (unsigned int)header, msgid, header->msgid, header->size); ++ vcos_assert(0); ++ } ++ } ++ } ++ ++ if (slot_queue_available != state->slot_queue_available) ++ { ++ state->slot_queue_available = slot_queue_available; ++ vcos_wmb(&state->slot_queue_available); ++ vcos_event_signal(&state->slot_available_event); ++ } ++} ++ ++/* Called by the slot handler and application threads */ ++static VCHIQ_STATUS_T ++queue_message(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, ++ int msgid, const VCHIQ_ELEMENT_T *elements, ++ int count, int size, int is_blocking) ++{ ++ VCHIQ_SHARED_STATE_T *local; ++ VCHIQ_SERVICE_QUOTA_T *service_quota = NULL; ++ VCHIQ_HEADER_T *header; ++ ++ unsigned int stride; ++ ++ local = state->local; ++ ++ stride = calc_stride(size); ++ ++ vcos_assert(stride <= VCHIQ_SLOT_SIZE); ++ ++ /* On platforms where vcos_mutex_lock cannot fail, the return will never ++ be taken and the compiler may optimise out that code. Let Coverity ++ know this is intentional. ++ */ ++ /* coverity[constant_expression_result] */ ++ if ((VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_RESUME) && ++ (vcos_mutex_lock(&state->slot_mutex) != VCOS_SUCCESS)) ++ return VCHIQ_RETRY; ++ ++ if (service) ++ { ++ int tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos + stride - 1); ++ ++ if (service->srvstate != VCHIQ_SRVSTATE_OPEN) ++ { ++ /* The service has been closed, probably while waiting for the mutex */ ++ vcos_mutex_unlock(&state->slot_mutex); ++ return VCHIQ_ERROR; ++ } ++ ++ service_quota = &state->service_quotas[service->localport]; ++ ++ /* ...ensure it doesn't use more than its quota of slots */ ++ while ((tx_end_index != service_quota->previous_tx_index) && ++ (service_quota->slot_use_count == service_quota->slot_quota)) ++ { ++ vcos_log_trace("%d: qm:%d %s,%x - quota stall", ++ state->id, service->localport, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), size); ++ VCHIQ_SERVICE_STATS_INC(service, quota_stalls); ++ vcos_mutex_unlock(&state->slot_mutex); ++ if (vcos_event_wait(&service_quota->quota_event) != VCOS_SUCCESS) ++ return VCHIQ_RETRY; ++ if (vcos_mutex_lock(&state->slot_mutex) != VCOS_SUCCESS) ++ return VCHIQ_RETRY; ++ vcos_assert(service_quota->slot_use_count <= service_quota->slot_quota); ++ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos + stride - 1); ++ } ++ } ++ ++ header = reserve_space(state, stride, is_blocking); ++ ++ if (!header) { ++ if (service) ++ VCHIQ_SERVICE_STATS_INC(service, slot_stalls); ++ vcos_mutex_unlock(&state->slot_mutex); ++ return VCHIQ_RETRY; ++ } ++ ++ if (service) { ++ int i, pos; ++ int tx_end_index; ++ ++ vcos_log_info("%d: qm %s@%x,%x (%d->%d)", state->id, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ (unsigned int)header, size, ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid)); ++ ++ for (i = 0, pos = 0; i < (unsigned int)count; ++ pos += elements[i++].size) ++ if (elements[i].size) { ++ if (vchiq_copy_from_user ++ (header->data + pos, elements[i].data, ++ (size_t) elements[i].size) != ++ VCHIQ_SUCCESS) { ++ vcos_mutex_unlock(&state->slot_mutex); ++ VCHIQ_SERVICE_STATS_INC(service, error_count); ++ return VCHIQ_ERROR; ++ } ++ if (i == 0) { ++ vcos_log_dump_mem( &vchiq_core_msg_log_category, ++ "Sent", 0, header->data + pos, ++ vcos_min( 64, elements[0].size )); ++ } ++ } ++ ++ /* If this transmission can't fit in the last slot used by this service... */ ++ tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos - 1); ++ if (tx_end_index != service_quota->previous_tx_index) ++ { ++ service_quota->slot_use_count++; ++ vcos_log_trace("%d: qm:%d %s,%x - slot_use->%d", ++ state->id, service->localport, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), size, ++ service_quota->slot_use_count); ++ } ++ ++ service_quota->previous_tx_index = tx_end_index; ++ VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size); ++ } else { ++ vcos_log_info("%d: qm %s@%x,%x (%d->%d)", state->id, ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ (unsigned int)header, size, ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid)); ++ if (size != 0) ++ { ++ vcos_assert((count == 1) && (size == elements[0].size)); ++ memcpy(header->data, elements[0].data, elements[0].size); ++ } ++ VCHIQ_STATS_INC(state, ctrl_tx_count); ++ } ++ ++ header->msgid = msgid; ++ header->size = size; ++ ++ if (vcos_is_log_enabled( &vchiq_core_msg_log_category, VCOS_LOG_INFO)) ++ { ++ int svc_fourcc; ++ ++ svc_fourcc = service ++ ? service->base.fourcc ++ : VCHIQ_MAKE_FOURCC('?','?','?','?'); ++ ++ vcos_log_impl( &vchiq_core_msg_log_category, ++ VCOS_LOG_INFO, ++ "Sent Msg %s(%u) to %c%c%c%c s:%u d:%d len:%d", ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), ++ VCHIQ_MSG_TYPE(msgid), ++ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), ++ VCHIQ_MSG_SRCPORT(msgid), ++ VCHIQ_MSG_DSTPORT(msgid), ++ size ); ++ } ++ ++ /* Make the new tx_pos visible to the peer. */ ++ local->tx_pos = state->local_tx_pos; ++ vcos_wmb(&local->tx_pos); ++ ++ if (VCHIQ_MSG_TYPE(msgid) != VCHIQ_MSG_PAUSE) ++ vcos_mutex_unlock(&state->slot_mutex); ++ ++ remote_event_signal(&state->remote->trigger); ++ ++ return VCHIQ_SUCCESS; ++} ++ ++static inline void ++claim_slot(VCHIQ_SLOT_INFO_T *slot) ++{ ++ slot->use_count++; ++} ++ ++static void ++release_slot(VCHIQ_STATE_T *state, VCHIQ_SLOT_INFO_T *slot_info) ++{ ++ int release_count; ++ vcos_mutex_lock(&state->recycle_mutex); ++ ++ release_count = slot_info->release_count; ++ slot_info->release_count = ++release_count; ++ ++ if (release_count == slot_info->use_count) ++ { ++ int slot_queue_recycle; ++ /* Add to the freed queue */ ++ ++ /* A read barrier is necessary here to prevent speculative fetches of ++ remote->slot_queue_recycle from overtaking the mutex. */ ++ vcos_rmb(); ++ ++ slot_queue_recycle = state->remote->slot_queue_recycle; ++ state->remote->slot_queue[slot_queue_recycle & VCHIQ_SLOT_QUEUE_MASK] = ++ SLOT_INDEX_FROM_INFO(state, slot_info); ++ state->remote->slot_queue_recycle = slot_queue_recycle + 1; ++ vcos_log_info("%d: release_slot %d - recycle->%x", ++ state->id, SLOT_INDEX_FROM_INFO(state, slot_info), ++ state->remote->slot_queue_recycle); ++ ++ /* A write barrier is necessary, but remote_event_signal contains one. */ ++ remote_event_signal(&state->remote->recycle); ++ } ++ ++ vcos_mutex_unlock(&state->recycle_mutex); ++} ++ ++/* Called by the slot handler - don't hold the bulk mutex */ ++static VCHIQ_STATUS_T ++notify_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ vcos_log_trace("%d: nb:%d %cx - p=%x rn=%x r=%x", ++ service->state->id, service->localport, ++ (queue == &service->bulk_tx) ? 't' : 'r', ++ queue->process, queue->remote_notify, queue->remove); ++ ++ if (service->state->is_master) ++ { ++ while (queue->remote_notify != queue->process) ++ { ++ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->remote_notify)]; ++ int msgtype = (bulk->dir == VCHIQ_BULK_TRANSMIT) ? ++ VCHIQ_MSG_BULK_RX_DONE : VCHIQ_MSG_BULK_TX_DONE; ++ int msgid = VCHIQ_MAKE_MSG(msgtype, service->localport, service->remoteport); ++ VCHIQ_ELEMENT_T element = { &bulk->actual, 4 }; ++ /* Only reply to non-dummy bulk requests */ ++ if (bulk->remote_data) ++ { ++ status = queue_message(service->state, NULL, msgid, &element, 1, 4, 0); ++ if (status != VCHIQ_SUCCESS) ++ break; ++ } ++ queue->remote_notify++; ++ } ++ } ++ else ++ { ++ queue->remote_notify = queue->process; ++ } ++ ++ if (status == VCHIQ_SUCCESS) ++ { ++ while (queue->remove != queue->remote_notify) ++ { ++ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->remove)]; ++ ++ /* Only generate callbacks for non-dummy bulk requests */ ++ if (bulk->data) ++ { ++ if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) ++ { ++ if (bulk->dir == VCHIQ_BULK_TRANSMIT) ++ { ++ VCHIQ_SERVICE_STATS_INC(service, bulk_tx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, bulk_tx_bytes, bulk->actual); ++ } ++ else ++ { ++ VCHIQ_SERVICE_STATS_INC(service, bulk_rx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, bulk_rx_bytes, bulk->actual); ++ } ++ } ++ else ++ { ++ VCHIQ_SERVICE_STATS_INC(service, bulk_aborted_count); ++ } ++ if (bulk->mode == VCHIQ_BULK_MODE_BLOCKING) ++ { ++ BULK_WAITER_T *waiter = (BULK_WAITER_T *)bulk->userdata; ++ if (waiter) ++ { ++ waiter->actual = bulk->actual; ++ vcos_event_signal(&waiter->event); ++ } ++ } ++ else if (bulk->mode == VCHIQ_BULK_MODE_CALLBACK) ++ { ++ VCHIQ_REASON_T reason = (bulk->dir == VCHIQ_BULK_TRANSMIT) ? ++ ((bulk->actual == VCHIQ_BULK_ACTUAL_ABORTED) ? ++ VCHIQ_BULK_TRANSMIT_ABORTED : VCHIQ_BULK_TRANSMIT_DONE) : ++ ((bulk->actual == VCHIQ_BULK_ACTUAL_ABORTED) ? ++ VCHIQ_BULK_RECEIVE_ABORTED : VCHIQ_BULK_RECEIVE_DONE); ++ status = make_service_callback(service, reason, ++ NULL, bulk->userdata); ++ if (status == VCHIQ_RETRY) ++ break; ++ } ++ } ++ ++ queue->remove++; ++ vcos_event_signal(&service->bulk_remove_event); ++ } ++ } ++ ++ if (status != VCHIQ_SUCCESS) ++ request_poll(service->state, service, (queue == &service->bulk_tx) ? ++ VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY); ++ ++ return status; ++} ++ ++/* Called by the slot handler thread */ ++static void ++poll_services(VCHIQ_STATE_T *state) ++{ ++ int group, i; ++ ++ for (group = 0; group < BITSET_SIZE(state->unused_service); group++) ++ { ++ uint32_t flags; ++ flags = vcos_atomic_flags_get_and_clear(&state->poll_services[group]); ++ for (i = 0; flags; i++) ++ { ++ if (flags & (1 << i)) ++ { ++ VCHIQ_SERVICE_T *service = state->services[(group<<5) + i]; ++ uint32_t service_flags = ++ vcos_atomic_flags_get_and_clear(&service->poll_flags); ++ if (service_flags & (1 << VCHIQ_POLL_TERMINATE)) ++ { ++ vcos_log_info("%d: ps - terminate %d<->%d", state->id, service->localport, service->remoteport); ++ if (vchiq_close_service_internal(service, 0/*!close_recvd*/) != VCHIQ_SUCCESS) ++ request_poll(state, service, VCHIQ_POLL_TERMINATE); ++ } ++ if (service_flags & (1 << VCHIQ_POLL_TXNOTIFY)) ++ notify_bulks(service, &service->bulk_tx); ++ if (service_flags & (1 << VCHIQ_POLL_RXNOTIFY)) ++ notify_bulks(service, &service->bulk_rx); ++ flags &= ~(1 << i); ++ } ++ } ++ } ++} ++ ++/* Called by the slot handler or application threads, holding the bulk mutex. */ ++static int ++resolve_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ int resolved = 0; ++ ++ while ((queue->process != queue->local_insert) && ++ (queue->process != queue->remote_insert)) ++ { ++ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)]; ++ ++ vcos_log_trace("%d: rb:%d %cx - li=%x ri=%x p=%x", ++ state->id, service->localport, ++ (queue == &service->bulk_tx) ? 't' : 'r', ++ queue->local_insert, queue->remote_insert, ++ queue->process); ++ ++ vcos_assert((int)(queue->local_insert - queue->process) > 0); ++ vcos_assert((int)(queue->remote_insert - queue->process) > 0); ++ vchiq_transfer_bulk(bulk); ++ ++ if (vcos_is_log_enabled( &vchiq_core_msg_log_category, VCOS_LOG_INFO)) ++ { ++ const char *header = (queue == &service->bulk_tx) ? ++ "Send Bulk to" : "Recv Bulk from"; ++ if (bulk->actual != VCHIQ_BULK_ACTUAL_ABORTED) ++ vcos_log_impl( &vchiq_core_msg_log_category, ++ VCOS_LOG_INFO, ++ "%s %c%c%c%c d:%d len:%d %x<->%x", ++ header, ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->remoteport, ++ bulk->size, ++ (unsigned int)bulk->data, ++ (unsigned int)bulk->remote_data ); ++ else ++ vcos_log_impl( &vchiq_core_msg_log_category, ++ VCOS_LOG_INFO, ++ "%s %c%c%c%c d:%d ABORTED - tx len:%d, rx len:%d %x<->%x", ++ header, ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->remoteport, ++ bulk->size, ++ bulk->remote_size, ++ (unsigned int)bulk->data, ++ (unsigned int)bulk->remote_data ); ++ } ++ ++ vchiq_complete_bulk(bulk); ++ queue->process++; ++ resolved++; ++ } ++ return resolved; ++} ++ ++/* Called with the bulk_mutex held */ ++static void ++abort_outstanding_bulks(VCHIQ_SERVICE_T *service, VCHIQ_BULK_QUEUE_T *queue) ++{ ++ int is_tx = (queue == &service->bulk_tx); ++ vcos_log_trace("%d: aob:%d %cx - li=%x ri=%x p=%x", ++ service->state->id, service->localport, is_tx ? 't' : 'r', ++ queue->local_insert, queue->remote_insert, queue->process); ++ ++ vcos_assert((int)(queue->local_insert - queue->process) >= 0); ++ vcos_assert((int)(queue->remote_insert - queue->process) >= 0); ++ ++ while ((queue->process != queue->local_insert) || ++ (queue->process != queue->remote_insert)) ++ { ++ VCHIQ_BULK_T *bulk = &queue->bulks[BULK_INDEX(queue->process)]; ++ ++ if (queue->process == queue->remote_insert) ++ { ++ /* fabricate a matching dummy bulk */ ++ bulk->remote_data = NULL; ++ bulk->remote_size = 0; ++ queue->remote_insert++; ++ } ++ ++ if (queue->process != queue->local_insert) ++ { ++ vchiq_complete_bulk(bulk); ++ ++ if (vcos_is_log_enabled( &vchiq_core_msg_log_category, VCOS_LOG_INFO)) ++ { ++ vcos_log_impl( &vchiq_core_msg_log_category, ++ VCOS_LOG_INFO, ++ "%s %c%c%c%c d:%d ABORTED - tx len:%d, rx len:%d", ++ is_tx ? "Send Bulk to" : "Recv Bulk from", ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->remoteport, ++ bulk->size, ++ bulk->remote_size ); ++ } ++ } ++ else ++ { ++ /* fabricate a matching dummy bulk */ ++ bulk->data = NULL; ++ bulk->size = 0; ++ bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED; ++ bulk->dir = is_tx ? VCHIQ_BULK_TRANSMIT : VCHIQ_BULK_RECEIVE; ++ queue->local_insert++; ++ } ++ ++ queue->process++; ++ } ++} ++ ++static void ++pause_bulks(VCHIQ_STATE_T *state) ++{ ++ int i; ++ ++ /* Block bulk transfers from all services */ ++ for (i = 0; i < state->unused_service; i++) ++ { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ if (!service || (service->srvstate != VCHIQ_SRVSTATE_OPEN)) ++ continue; ++ ++ vcos_log_trace("locking bulk_mutex for service %d", i); ++ vcos_mutex_lock(&service->bulk_mutex); ++ } ++} ++ ++static void ++resume_bulks(VCHIQ_STATE_T *state) ++{ ++ int i; ++ ++ /* Poll all services in case any bulk transfers have been ++ deferred */ ++ for (i = 0; i < state->unused_service; i++) ++ { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ if (!service || (service->srvstate != VCHIQ_SRVSTATE_OPEN)) ++ continue; ++ ++ if (resolve_bulks(service, &service->bulk_tx)) ++ request_poll(state, service, VCHIQ_POLL_TXNOTIFY); ++ if (resolve_bulks(service, &service->bulk_rx)) ++ request_poll(state, service, VCHIQ_POLL_RXNOTIFY); ++ vcos_log_trace("unlocking bulk_mutex for service %d", i); ++ vcos_mutex_unlock(&service->bulk_mutex); ++ } ++} ++ ++/* Called by the slot handler thread */ ++static void ++parse_rx_slots(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_SHARED_STATE_T *remote = state->remote; ++ int tx_pos; ++ DEBUG_INITIALISE(state->local) ++ ++ tx_pos = remote->tx_pos; ++ ++ while (state->rx_pos != tx_pos) { ++ VCHIQ_SERVICE_T *service = NULL; ++ VCHIQ_HEADER_T *header; ++ int msgid, size; ++ int type; ++ unsigned int localport, remoteport; ++ ++ DEBUG_TRACE(PARSE_LINE); ++ if (!state->rx_data) ++ { ++ int rx_index; ++ vcos_assert((state->rx_pos & VCHIQ_SLOT_MASK) == 0); ++ rx_index = remote->slot_queue[SLOT_QUEUE_INDEX_FROM_POS(state->rx_pos) & VCHIQ_SLOT_QUEUE_MASK]; ++ state->rx_data = (char *)SLOT_DATA_FROM_INDEX(state, rx_index); ++ state->rx_info = SLOT_INFO_FROM_INDEX(state, rx_index); ++ ++ /* Initialise use_count to one, and increment release_count at the end ++ of the slot to avoid releasing the slot prematurely. */ ++ state->rx_info->use_count = 1; ++ state->rx_info->release_count = 0; ++ } ++ ++ header = (VCHIQ_HEADER_T *)(state->rx_data + (state->rx_pos & VCHIQ_SLOT_MASK)); ++ DEBUG_VALUE(PARSE_HEADER, (int)header); ++ msgid = header->msgid; ++ DEBUG_VALUE(PARSE_MSGID, msgid); ++ size = header->size; ++ type = VCHIQ_MSG_TYPE(msgid); ++ localport = VCHIQ_MSG_DSTPORT(msgid); ++ remoteport = VCHIQ_MSG_SRCPORT(msgid); ++ ++ if (type != VCHIQ_MSG_DATA) ++ { ++ VCHIQ_STATS_INC(state, ctrl_rx_count); ++ } ++ ++ switch (type) ++ { ++ case VCHIQ_MSG_OPENACK: ++ case VCHIQ_MSG_CLOSE: ++ case VCHIQ_MSG_DATA: ++ case VCHIQ_MSG_BULK_RX: ++ case VCHIQ_MSG_BULK_TX: ++ case VCHIQ_MSG_BULK_RX_DONE: ++ case VCHIQ_MSG_BULK_TX_DONE: ++ if (localport <= VCHIQ_PORT_MAX) ++ { ++ service = state->services[localport]; ++ if (service && (service->srvstate == VCHIQ_SRVSTATE_FREE)) ++ service = NULL; ++ } ++ if (!service) ++ { ++ vcos_log_error( ++ "%d: prs %s@%x (%d->%d) - invalid/closed service %d", ++ state->id, msg_type_str(type), (unsigned int)header, ++ remoteport, localport, localport); ++ goto skip_message; ++ } ++ default: ++ break; ++ } ++ ++ if ( vcos_is_log_enabled( &vchiq_core_msg_log_category, VCOS_LOG_INFO)) ++ { ++ int svc_fourcc; ++ ++ svc_fourcc = service ++ ? service->base.fourcc ++ : VCHIQ_MAKE_FOURCC('?','?','?','?'); ++ vcos_log_impl( &vchiq_core_msg_log_category, ++ VCOS_LOG_INFO, ++ "Rcvd Msg %s(%u) from %c%c%c%c s:%d d:%d len:%d", ++ msg_type_str(type), type, ++ VCHIQ_FOURCC_AS_4CHARS(svc_fourcc), ++ remoteport, localport, size ); ++ if (size > 0) { ++ vcos_log_dump_mem( &vchiq_core_msg_log_category, ++ "Rcvd", 0, header->data, ++ vcos_min( 64, size )); ++ } ++ } ++ ++ if (((unsigned int)header & VCHIQ_SLOT_MASK) + calc_stride(size) > VCHIQ_SLOT_SIZE) ++ { ++ vcos_log_error("header %x (msgid %x) - size %x too big for slot", ++ (unsigned int)header, (unsigned int)msgid, (unsigned int)size); ++ vcos_assert(0); ++ } ++ ++ switch (type) { ++ case VCHIQ_MSG_OPEN: ++ vcos_assert(VCHIQ_MSG_DSTPORT(msgid) == 0); ++ if (vcos_verify(size == sizeof(VCHIQ_OPEN_PAYLOAD_T))) { ++ const VCHIQ_OPEN_PAYLOAD_T *payload = (VCHIQ_OPEN_PAYLOAD_T *)header->data; ++ unsigned int fourcc; ++ ++ fourcc = payload->fourcc; ++ vcos_log_info("%d: prs OPEN@%x (%d->'%c%c%c%c')", ++ state->id, (unsigned int)header, ++ localport, ++ VCHIQ_FOURCC_AS_4CHARS(fourcc)); ++ ++ service = get_listening_service(state, fourcc); ++ ++ if (service) ++ { ++ /* A matching service exists */ ++ short version = payload->version; ++ short version_min = payload->version_min; ++ if ((service->version < version_min) || ++ (version < service->version_min)) ++ { ++ /* Version mismatch */ ++ vcos_log_error("%d: service %d (%c%c%c%c) version mismatch -" ++ " local (%d, min %d) vs. remote (%d, min %d)", ++ state->id, service->localport, ++ VCHIQ_FOURCC_AS_4CHARS(fourcc), ++ service->version, service->version_min, ++ version, version_min); ++ goto fail_open; ++ } ++ if (service->srvstate == VCHIQ_SRVSTATE_LISTENING) ++ { ++ /* Acknowledge the OPEN */ ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_OPENACK, service->localport, remoteport), ++ NULL, 0, 0, 0) == VCHIQ_RETRY) ++ return; /* Bail out if not ready */ ++ ++ /* The service is now open */ ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_OPEN); ++ } ++ ++ service->remoteport = remoteport; ++ service->client_id = ((int *)header->data)[1]; ++ if (make_service_callback(service, VCHIQ_SERVICE_OPENED, ++ NULL, NULL) == VCHIQ_RETRY) ++ { ++ /* Bail out if not ready */ ++ service->remoteport = VCHIQ_PORT_FREE; ++ return; ++ } ++ ++ /* Break out, and skip the failure handling */ ++ break; ++ } ++ } ++ fail_open: ++ /* No available service, or an invalid request - send a CLOSE */ ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_CLOSE, 0, VCHIQ_MSG_SRCPORT(msgid)), ++ NULL, 0, 0, 0) == VCHIQ_RETRY) ++ return; /* Bail out if not ready */ ++ break; ++ case VCHIQ_MSG_OPENACK: ++ { ++ vcos_log_info("%d: prs OPENACK@%x (%d->%d)", ++ state->id, (unsigned int)header, ++ remoteport, localport); ++ if (service->srvstate == VCHIQ_SRVSTATE_OPENING) { ++ service->remoteport = remoteport; ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_OPEN); ++ vcos_event_signal(&service->remove_event); ++ } ++ } ++ break; ++ case VCHIQ_MSG_CLOSE: ++ { ++ vcos_assert(size == 0); /* There should be no data */ ++ ++ vcos_log_info("%d: prs CLOSE@%x (%d->%d)", ++ state->id, (unsigned int)header, ++ remoteport, localport); ++ ++ if ((service->remoteport != remoteport) && ++ VCHIQ_PORT_IS_VALID(service->remoteport)) { ++ /* This could be from a client which hadn't yet received ++ the OPENACK - look for the connected service */ ++ service = get_connected_service(state, remoteport); ++ if (!service) ++ break; ++ } ++ ++ if (vchiq_close_service_internal(service, ++ 1/*close_recvd*/) == VCHIQ_RETRY) ++ return; /* Bail out if not ready */ ++ ++ if (vcos_is_log_enabled( &vchiq_core_msg_log_category, VCOS_LOG_INFO)) ++ { ++ vcos_log_impl( &vchiq_core_msg_log_category, ++ VCOS_LOG_INFO, ++ "Close Service %c%c%c%c s:%u d:%d", ++ VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), ++ service->localport, ++ service->remoteport ); ++ } ++ } ++ break; ++ case VCHIQ_MSG_DATA: ++ { ++ vcos_log_trace("%d: prs DATA@%x,%x (%d->%d)", ++ state->id, (unsigned int)header, size, ++ remoteport, localport); ++ ++ if ((service->remoteport == remoteport) ++ && (service->srvstate == ++ VCHIQ_SRVSTATE_OPEN)) { ++ header->msgid = msgid | VCHIQ_MSGID_CLAIMED; ++ claim_slot(state->rx_info); ++ DEBUG_TRACE(PARSE_LINE); ++ if (make_service_callback(service, ++ VCHIQ_MESSAGE_AVAILABLE, header, ++ NULL) == VCHIQ_RETRY) ++ { ++ DEBUG_TRACE(PARSE_LINE); ++ return; /* Bail out if not ready */ ++ } ++ VCHIQ_SERVICE_STATS_INC(service, ctrl_rx_count); ++ VCHIQ_SERVICE_STATS_ADD(service, ctrl_rx_bytes, size); ++ } ++ else ++ { ++ VCHIQ_STATS_INC(state, error_count); ++ } ++ } ++ break; ++ case VCHIQ_MSG_CONNECT: ++ vcos_log_info("%d: prs CONNECT@%x", ++ state->id, (unsigned int)header); ++ vcos_event_signal(&state->connect); ++ break; ++ case VCHIQ_MSG_BULK_RX: ++ case VCHIQ_MSG_BULK_TX: ++ { ++ VCHIQ_BULK_QUEUE_T *queue; ++ vcos_assert(state->is_master); ++ queue = (type == VCHIQ_MSG_BULK_RX) ? ++ &service->bulk_tx : &service->bulk_rx; ++ if ((service->remoteport == remoteport) ++ && (service->srvstate == ++ VCHIQ_SRVSTATE_OPEN)) ++ { ++ VCHIQ_BULK_T *bulk; ++ int resolved; ++ ++ vcos_assert(queue->remote_insert < queue->remove + ++ VCHIQ_NUM_SERVICE_BULKS); ++ bulk = &queue->bulks[BULK_INDEX(queue->remote_insert)]; ++ bulk->remote_data = (void *)((int *)header->data)[0]; ++ bulk->remote_size = ((int *)header->data)[1]; ++ ++ vcos_log_info("%d: prs %s@%x (%d->%d) %x@%x", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, ++ bulk->remote_size, ++ (unsigned int)bulk->remote_data); ++ ++ queue->remote_insert++; ++ ++ if (state->conn_state != VCHIQ_CONNSTATE_CONNECTED) ++ break; ++ ++ DEBUG_TRACE(PARSE_LINE); ++ if (vcos_mutex_lock(&service->bulk_mutex) != VCOS_SUCCESS) ++ { ++ DEBUG_TRACE(PARSE_LINE); ++ return; ++ } ++ DEBUG_TRACE(PARSE_LINE); ++ resolved = resolve_bulks(service, queue); ++ vcos_mutex_unlock(&service->bulk_mutex); ++ if (resolved) ++ notify_bulks(service, queue); ++ } ++ } ++ break; ++ case VCHIQ_MSG_BULK_RX_DONE: ++ case VCHIQ_MSG_BULK_TX_DONE: ++ { ++ vcos_assert(!state->is_master); ++ if ((service->remoteport == remoteport) ++ && (service->srvstate != ++ VCHIQ_SRVSTATE_FREE)) { ++ VCHIQ_BULK_QUEUE_T *queue; ++ VCHIQ_BULK_T *bulk; ++ ++ queue = (type == VCHIQ_MSG_BULK_RX_DONE) ? ++ &service->bulk_rx : &service->bulk_tx; ++ ++ bulk = &queue->bulks[BULK_INDEX(queue->process)]; ++ bulk->actual = *(int *)header->data; ++ ++ vcos_log_info("%d: prs %s@%x (%d->%d) %x@%x", ++ state->id, msg_type_str(type), ++ (unsigned int)header, ++ remoteport, localport, ++ bulk->actual, (unsigned int)bulk->data); ++ ++ vcos_log_trace("%d: prs:%d %cx li=%x ri=%x p=%x", ++ state->id, localport, ++ (type == VCHIQ_MSG_BULK_RX_DONE) ? 'r' : 't', ++ queue->local_insert, ++ queue->remote_insert, queue->process); ++ ++ DEBUG_TRACE(PARSE_LINE); ++ if (vcos_mutex_lock(&service->bulk_mutex) != VCOS_SUCCESS) ++ { ++ DEBUG_TRACE(PARSE_LINE); ++ return; ++ } ++ DEBUG_TRACE(PARSE_LINE); ++ vcos_assert(queue->process != queue->local_insert); ++ vchiq_complete_bulk(bulk); ++ queue->process++; ++ vcos_mutex_unlock(&service->bulk_mutex); ++ DEBUG_TRACE(PARSE_LINE); ++ notify_bulks(service, queue); ++ DEBUG_TRACE(PARSE_LINE); ++ } ++ } ++ break; ++ case VCHIQ_MSG_PADDING: ++ vcos_log_trace("%d: prs PADDING@%x,%x", ++ state->id, (unsigned int)header, size); ++ break; ++ case VCHIQ_MSG_PAUSE: ++ /* If initiated, signal the application thread */ ++ vcos_log_trace("%d: prs PAUSE@%x,%x", ++ state->id, (unsigned int)header, size); ++ if (state->conn_state != VCHIQ_CONNSTATE_PAUSE_SENT) ++ { ++ /* Send a PAUSE in response */ ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), ++ NULL, 0, 0, 0) == VCHIQ_RETRY) ++ return; /* Bail out if not ready */ ++ if (state->is_master) ++ pause_bulks(state); ++ } ++ /* At this point slot_mutex is held */ ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSED); ++ vchiq_platform_paused(state); ++ break; ++ case VCHIQ_MSG_RESUME: ++ vcos_log_trace("%d: prs RESUME@%x,%x", ++ state->id, (unsigned int)header, size); ++ /* Release the slot mutex */ ++ vcos_mutex_unlock(&state->slot_mutex); ++ if (state->is_master) ++ resume_bulks(state); ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); ++ vchiq_platform_resumed(state); ++ break; ++ default: ++ vcos_log_error("%d: prs invalid msgid %x@%x,%x", ++ state->id, msgid, (unsigned int)header, size); ++ vcos_assert(0); ++ break; ++ } ++ ++ skip_message: ++ state->rx_pos += calc_stride(size); ++ ++ DEBUG_TRACE(PARSE_LINE); ++ /* Perform some housekeeping when the end of the slot is reached. */ ++ if ((state->rx_pos & VCHIQ_SLOT_MASK) == 0) ++ { ++ /* Remove the extra reference count. */ ++ release_slot(state, state->rx_info); ++ state->rx_data = NULL; ++ } ++ } ++} ++ ++/* Called by the slot handler thread */ ++static void * ++slot_handler_func(void *v) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; ++ VCHIQ_SHARED_STATE_T *local = state->local; ++ DEBUG_INITIALISE(local) ++ ++ while (1) { ++ DEBUG_COUNT(SLOT_HANDLER_COUNT); ++ DEBUG_TRACE(SLOT_HANDLER_LINE); ++ remote_event_wait(&local->trigger); ++ ++ vcos_rmb(); ++ ++ DEBUG_TRACE(SLOT_HANDLER_LINE); ++ if (state->poll_needed) ++ { ++ state->poll_needed = 0; ++ ++ /* Handle service polling and other rare conditions here out ++ of the mainline code */ ++ switch (state->conn_state) ++ { ++ case VCHIQ_CONNSTATE_CONNECTED: ++ /* Poll the services as requested */ ++ poll_services(state); ++ break; ++ ++ case VCHIQ_CONNSTATE_PAUSING: ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_PAUSE, 0, 0), NULL, 0, 0, 0) ++ != VCHIQ_RETRY) ++ { ++ if (state->is_master) ++ pause_bulks(state); ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSE_SENT); ++ } ++ else ++ { ++ state->poll_needed = 1; /* Retry later */ ++ } ++ break; ++ ++ case VCHIQ_CONNSTATE_RESUMING: ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_RESUME, 0, 0), NULL, 0, 0, 0) ++ != VCHIQ_RETRY) ++ { ++ if (state->is_master) ++ resume_bulks(state); ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); ++ vchiq_platform_resumed(state); ++ } ++ else ++ { ++ /* This should really be impossible, since the PAUSE should ++ have flushed through outstanding messages. */ ++ vcos_log_error("Failed to send RESUME message"); ++ vcos_demand(0); ++ } ++ break; ++ default: ++ break; ++ } ++ } ++ ++ DEBUG_TRACE(SLOT_HANDLER_LINE); ++ parse_rx_slots(state); ++ } ++ return NULL; ++} ++ ++extern VCHIQ_STATUS_T ++vchiq_platform_suspend(VCHIQ_STATE_T *state); ++ ++/* Called by the recycle thread */ ++static void * ++recycle_func(void *v) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; ++ VCHIQ_SHARED_STATE_T *local = state->local; ++ ++ while (1) { ++ remote_event_wait(&local->recycle); ++ ++ vcos_mutex_lock(&state->slot_mutex); ++ ++ process_free_queue(state); ++ ++ vcos_mutex_unlock(&state->slot_mutex); ++ } ++ return NULL; ++} ++ ++/* Called by the lp thread */ ++static void * ++lp_func(void *v) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; ++ ++ while (1) { ++ vcos_event_wait(&state->lp_evt); ++ vcos_mutex_lock(&state->use_count_mutex); ++ if (state->videocore_use_count == 0) ++ { ++ vchiq_platform_suspend(state); ++ } ++ vcos_mutex_unlock(&state->use_count_mutex); ++ } ++ return NULL; ++} ++ ++static void ++init_bulk_queue(VCHIQ_BULK_QUEUE_T *queue) ++{ ++ queue->local_insert = 0; ++ queue->remote_insert = 0; ++ queue->process = 0; ++ queue->remote_notify = 0; ++ queue->remove = 0; ++} ++ ++VCHIQ_SLOT_ZERO_T * ++vchiq_init_slots(void *mem_base, int mem_size) ++{ ++ int mem_align = (VCHIQ_SLOT_SIZE - (int)mem_base) & VCHIQ_SLOT_MASK; ++ VCHIQ_SLOT_ZERO_T *slot_zero = (VCHIQ_SLOT_ZERO_T *)((char *)mem_base + mem_align); ++ int num_slots = (mem_size - mem_align)/VCHIQ_SLOT_SIZE; ++ int first_data_slot = VCHIQ_SLOT_ZERO_SLOTS; ++ ++ /* Ensure there is enough memory to run an absolutely minimum system */ ++ num_slots -= first_data_slot; ++ ++ if (num_slots < 4) ++ { ++ vcos_log_error("vchiq_init_slots - insufficient memory %x bytes", mem_size); ++ return NULL; ++ } ++ ++ memset(slot_zero, 0, sizeof(VCHIQ_SLOT_ZERO_T)); ++ ++ slot_zero->magic = VCHIQ_MAGIC; ++ slot_zero->version = VCHIQ_VERSION; ++ slot_zero->version_min = VCHIQ_VERSION_MIN; ++ slot_zero->slot_zero_size = sizeof(VCHIQ_SLOT_ZERO_T); ++ slot_zero->slot_size = VCHIQ_SLOT_SIZE; ++ slot_zero->max_slots = VCHIQ_MAX_SLOTS; ++ slot_zero->max_slots_per_side = VCHIQ_MAX_SLOTS_PER_SIDE; ++ ++ slot_zero->master.slot_first = first_data_slot; ++ slot_zero->slave.slot_first = first_data_slot + (num_slots/2); ++ slot_zero->master.slot_last = slot_zero->slave.slot_first - 1; ++ slot_zero->slave.slot_last = first_data_slot + num_slots - 1; ++ ++ return slot_zero; ++} ++ ++VCHIQ_STATUS_T ++vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, int is_master) ++{ ++ VCHIQ_SHARED_STATE_T *local; ++ VCHIQ_SHARED_STATE_T *remote; ++ VCOS_THREAD_ATTR_T attrs; ++ char threadname[10]; ++ static int id = 0; ++ int i; ++ ++ vcos_log_set_level(&vchiq_core_log_category, vchiq_default_core_log_level); ++ vcos_log_set_level(&vchiq_core_msg_log_category, vchiq_default_core_msg_log_level); ++ vcos_log_register("vchiq_core", &vchiq_core_log_category); ++ vcos_log_register("vchiq_core_msg", &vchiq_core_msg_log_category); ++ ++ vcos_log_warn( "%s: slot_zero = 0x%08lx, is_master = %d\n", __func__, (unsigned long)slot_zero, is_master ); ++ ++ /* Check the input configuration */ ++ ++ if (slot_zero->magic != VCHIQ_MAGIC) ++ { ++ vcos_log_error("slot_zero=%x: magic=%x (expected %x)", ++ (unsigned int)slot_zero, slot_zero->magic, VCHIQ_MAGIC); ++ return VCHIQ_ERROR; ++ } ++ ++ if (slot_zero->version < VCHIQ_VERSION_MIN) ++ { ++ vcos_log_error("slot_zero=%x: peer_version=%x (minimum %x)", ++ (unsigned int)slot_zero, slot_zero->version, VCHIQ_VERSION_MIN); ++ return VCHIQ_ERROR; ++ } ++ ++ if (VCHIQ_VERSION < slot_zero->version_min) ++ { ++ vcos_log_error("slot_zero=%x: version=%x (peer minimum %x)", ++ (unsigned int)slot_zero, VCHIQ_VERSION, slot_zero->version_min); ++ return VCHIQ_ERROR; ++ } ++ ++ if (slot_zero->slot_zero_size != sizeof(VCHIQ_SLOT_ZERO_T)) ++ { ++ vcos_log_error("slot_zero=%x: slot_zero_size=%x (expected %x)", ++ (unsigned int)slot_zero, slot_zero->slot_zero_size, sizeof(VCHIQ_SLOT_ZERO_T)); ++ return VCHIQ_ERROR; ++ } ++ ++ if (slot_zero->slot_size != VCHIQ_SLOT_SIZE) ++ { ++ vcos_log_error("slot_zero=%x: slot_size=%d (expected %d", ++ (unsigned int)slot_zero, slot_zero->slot_size, VCHIQ_SLOT_SIZE); ++ return VCHIQ_ERROR; ++ } ++ ++ if (slot_zero->max_slots != VCHIQ_MAX_SLOTS) ++ { ++ vcos_log_error("slot_zero=%x: max_slots=%d (expected %d)", ++ (unsigned int)slot_zero, slot_zero->max_slots, VCHIQ_MAX_SLOTS); ++ return VCHIQ_ERROR; ++ } ++ ++ if (slot_zero->max_slots_per_side != VCHIQ_MAX_SLOTS_PER_SIDE) ++ { ++ vcos_log_error("slot_zero=%x: max_slots_per_side=%d (expected %d)", ++ (unsigned int)slot_zero, slot_zero->max_slots_per_side, ++ VCHIQ_MAX_SLOTS_PER_SIDE); ++ return VCHIQ_ERROR; ++ } ++ ++ if (is_master) ++ { ++ local = &slot_zero->master; ++ remote = &slot_zero->slave; ++ } ++ else ++ { ++ local = &slot_zero->slave; ++ remote = &slot_zero->master; ++ } ++ ++ if (local->initialised) ++ { ++ if (remote->initialised) ++ vcos_log_error("vchiq: FATAL: local state has already been initialised"); ++ else ++ vcos_log_error("vchiq: FATAL: master/slave mismatch - two %ss", is_master ? "master" : "slave"); ++ return VCHIQ_ERROR; ++ } ++ ++ memset(state, 0, sizeof(VCHIQ_STATE_T)); ++ state->id = id++; ++ state->is_master = is_master; ++ ++ /* ++ initialize shared state pointers ++ */ ++ ++ state->local = local; ++ state->remote = remote; ++ state->slot_data = (VCHIQ_SLOT_T *)slot_zero; ++ ++ /* ++ initialize events and mutexes ++ */ ++ ++ vcos_event_create(&state->connect, "v.connect"); ++ vcos_mutex_create(&state->mutex, "v.mutex"); ++ vcos_event_create(&state->trigger_event, "v.trigger_event"); ++ vcos_event_create(&state->recycle_event, "v.recycle_event"); ++ ++ vcos_mutex_create(&state->slot_mutex, "v.slot_mutex"); ++ vcos_mutex_create(&state->recycle_mutex, "v.recycle_mutex"); ++ vcos_mutex_create(&state->use_count_mutex, "v.use_count_mutex"); ++ vcos_mutex_create(&state->suspend_resume_mutex, "v.susp_res_mutex"); ++ ++ vcos_event_create(&state->slot_available_event, "v.slot_available_event"); ++ vcos_event_create(&state->slot_remove_event, "v.slot_remove_event"); ++ ++ state->slot_queue_available = 0; ++ ++ for (i = 0; i < VCHIQ_MAX_SERVICES; i++) ++ { ++ VCHIQ_SERVICE_QUOTA_T *service_quota = &state->service_quotas[i]; ++ vcos_event_create(&service_quota->quota_event, "v.quota_event"); ++ } ++ ++ for (i = local->slot_first; i <= local->slot_last; i++) ++ { ++ local->slot_queue[state->slot_queue_available++] = i; ++ } ++ ++ state->default_slot_quota = state->slot_queue_available/2; ++ ++ local->trigger.event = &state->trigger_event; ++ remote_event_create(&local->trigger); ++ local->tx_pos = 0; ++ ++ local->recycle.event = &state->recycle_event; ++ remote_event_create(&local->recycle); ++ local->slot_queue_recycle = state->slot_queue_available; ++ ++ vcos_event_create(&state->lp_evt, "LP_EVT"); ++ ++ local->debug[DEBUG_ENTRIES] = DEBUG_MAX; ++ ++ /* ++ bring up slot handler thread ++ */ ++ ++ vcos_thread_attr_init(&attrs); ++ vcos_thread_attr_setstacksize(&attrs, VCHIQ_SLOT_HANDLER_STACK); ++ vcos_thread_attr_setpriority(&attrs, VCOS_THREAD_PRI_REALTIME); ++ vcos_snprintf(threadname, sizeof(threadname), "VCHIQ-%d", state->id); ++ if (vcos_thread_create(&state->slot_handler_thread, threadname, ++ &attrs, slot_handler_func, state) != VCOS_SUCCESS) ++ return VCHIQ_ERROR; ++ ++ vcos_thread_attr_init(&attrs); ++ vcos_thread_attr_setstacksize(&attrs, VCHIQ_SLOT_HANDLER_STACK); ++ vcos_thread_attr_setpriority(&attrs, VCOS_THREAD_PRI_REALTIME); ++ vcos_snprintf(threadname, sizeof(threadname), "VCHIQr-%d", state->id); ++ if (vcos_thread_create(&state->recycle_thread, threadname, ++ &attrs, recycle_func, state) != VCOS_SUCCESS) ++ return VCHIQ_ERROR; ++ ++ vcos_thread_attr_init(&attrs); ++ vcos_thread_attr_setstacksize(&attrs, VCHIQ_SLOT_HANDLER_STACK); ++ vcos_thread_attr_setpriority(&attrs, VCOS_THREAD_PRI_LOWEST); ++ vcos_snprintf(threadname, sizeof(threadname), "VCHIQl-%d", state->id); ++ if (vcos_thread_create(&state->lp_thread, threadname, ++ &attrs, lp_func, state) != VCOS_SUCCESS) ++ return VCHIQ_ERROR; ++ ++ /* Indicate readiness to the other side */ ++ local->initialised = 1; ++ ++ return VCHIQ_SUCCESS; ++} ++ ++/* Called from application thread when a client or server service is created. */ ++VCHIQ_SERVICE_T * ++vchiq_add_service_internal(VCHIQ_STATE_T *state, ++ const VCHIQ_SERVICE_PARAMS_T *params, int srvstate, ++ VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_SERVICE_T **pservice = NULL; ++ VCHIQ_SERVICE_T *service = NULL; ++ int i; ++ ++ /* Prepare to use a previously unused service */ ++ if (state->unused_service < VCHIQ_MAX_SERVICES) ++ { ++ pservice = &state->services[state->unused_service]; ++ } ++ ++ if (srvstate == VCHIQ_SRVSTATE_OPENING) { ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *srv = state->services[i]; ++ if (!srv) ++ { ++ pservice = &state->services[i]; ++ break; ++ } ++ if (srv->srvstate == VCHIQ_SRVSTATE_FREE) { ++ service = srv; ++ break; ++ } ++ } ++ } else { ++ for (i = (state->unused_service - 1); i >= 0; i--) { ++ VCHIQ_SERVICE_T *srv = state->services[i]; ++ if (!srv) ++ pservice = &state->services[i]; ++ else if (srv->srvstate == VCHIQ_SRVSTATE_FREE) { ++ service = srv; ++ } else if ((srv->public_fourcc == params->fourcc) && ++ ((srv->instance != instance) ++ || (srv->base.callback != params->callback))) { ++ /* There is another server using this fourcc which doesn't match */ ++ pservice = NULL; ++ service = NULL; ++ } ++ } ++ } ++ ++ if (pservice && !service) ++ { ++ service = vcos_malloc(sizeof(VCHIQ_SERVICE_T), "VCHIQ service"); ++ if (service) ++ { ++ service->srvstate = VCHIQ_SRVSTATE_FREE; ++ service->localport = (pservice - state->services); ++ vcos_event_create(&service->remove_event, "v.remove_event"); ++ vcos_event_create(&service->bulk_remove_event, "v.bulk_remove_event"); ++ vcos_mutex_create(&service->bulk_mutex, "v.bulk_mutex"); ++ *pservice = service; ++ } ++ else ++ { ++ vcos_log_error("vchiq: Out of memory"); ++ } ++ } ++ ++ if (service) { ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &state->service_quotas[service->localport]; ++ if (vcos_is_log_enabled( &vchiq_core_msg_log_category, VCOS_LOG_INFO)) { ++ vcos_log_impl( &vchiq_core_msg_log_category, ++ VCOS_LOG_INFO, ++ "%s Service %c%c%c%c SrcPort:%d", ++ ( srvstate == VCHIQ_SRVSTATE_OPENING ) ++ ? "Open" : "Add", ++ VCHIQ_FOURCC_AS_4CHARS(params->fourcc), ++ service->localport ); ++ } ++ service->state = state; ++ service->base.fourcc = params->fourcc; ++ service->base.callback = params->callback; ++ service->base.userdata = params->userdata; ++ service->version = params->version; ++ service->version_min = params->version_min; ++ vchiq_set_service_state(service, srvstate); ++ service->public_fourcc = ++ (srvstate == ++ VCHIQ_SRVSTATE_OPENING) ? VCHIQ_FOURCC_INVALID : params->fourcc; ++ service->instance = instance; ++ service->remoteport = VCHIQ_PORT_FREE; ++ service->client_id = 0; ++ service->auto_close = 1; ++ service->service_use_count = 0; ++ init_bulk_queue(&service->bulk_tx); ++ init_bulk_queue(&service->bulk_rx); ++ service_quota->slot_quota = state->default_slot_quota; ++ if (service_quota->slot_use_count == 0) ++ service_quota->previous_tx_index = ++ SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos) - 1; ++ memset(&service->stats, 0, sizeof(service->stats)); ++ vcos_atomic_flags_create(&service->poll_flags); ++ ++ /* Ensure the events are unsignalled */ ++ while (vcos_event_try(&service->remove_event) == VCOS_SUCCESS) ++ continue; ++ while (vcos_event_try(&service_quota->quota_event) == VCOS_SUCCESS) ++ continue; ++ ++ if (pservice == &state->services[state->unused_service]) ++ state->unused_service++; ++ } ++ ++ return service; ++} ++ ++VCHIQ_STATUS_T ++vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id) ++{ ++ VCHIQ_OPEN_PAYLOAD_T payload = { ++ service->base.fourcc, ++ client_id, ++ service->version, ++ service->version_min ++ }; ++ VCHIQ_ELEMENT_T body = { &payload, sizeof(payload) }; ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ service->client_id = client_id; ++ vchiq_use_service(&service->base); ++ status = queue_message(service->state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_OPEN, service->localport, 0), ++ &body, 1, sizeof(payload), 1); ++ if (status == VCHIQ_SUCCESS) { ++ if (vcos_event_wait(&service->remove_event) != VCOS_SUCCESS) { ++ status = VCHIQ_RETRY; ++ vchiq_release_service(&service->base); ++ } else if (service->srvstate != VCHIQ_SRVSTATE_OPEN) { ++ vcos_log_info("%d: osi - srvstate = %d", service->state->id, service->srvstate); ++ vcos_assert(service->srvstate == VCHIQ_SRVSTATE_CLOSEWAIT); ++ status = VCHIQ_ERROR; ++ VCHIQ_SERVICE_STATS_INC(service, error_count); ++ vchiq_release_service(&service->base); ++ } ++ } ++ return status; ++} ++ ++/* Called by the slot handler */ ++VCHIQ_STATUS_T ++vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ vcos_log_trace("%d: csi:%d (%s)", ++ service->state->id, service->localport, ++ srvstate_names[service->srvstate]); ++ ++ switch (service->srvstate) ++ { ++ case VCHIQ_SRVSTATE_OPENING: ++ if (close_recvd) ++ { ++ /* The open was rejected - tell the user */ ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSEWAIT); ++ vcos_event_signal(&service->remove_event); ++ } ++ else ++ { ++ /* Shutdown mid-open - let the other side know */ ++ status = queue_message(state, NULL, ++ VCHIQ_MAKE_MSG ++ (VCHIQ_MSG_CLOSE, ++ service->localport, ++ VCHIQ_MSG_DSTPORT(service->remoteport)), ++ NULL, 0, 0, 0); ++ ++ if (status == VCHIQ_SUCCESS) ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSESENT); ++ } ++ break; ++ ++ case VCHIQ_SRVSTATE_OPEN: ++ if (state->is_master) ++ { ++ /* Abort any outstanding bulk transfers */ ++ vcos_mutex_lock(&service->bulk_mutex); ++ abort_outstanding_bulks(service, &service->bulk_tx); ++ abort_outstanding_bulks(service, &service->bulk_rx); ++ status = notify_bulks(service, &service->bulk_tx); ++ if (status == VCHIQ_SUCCESS) ++ status = notify_bulks(service, &service->bulk_rx); ++ vcos_mutex_unlock(&service->bulk_mutex); ++ } ++ ++ if (status == VCHIQ_SUCCESS) ++ status = queue_message(state, NULL, ++ VCHIQ_MAKE_MSG ++ (VCHIQ_MSG_CLOSE, ++ service->localport, ++ VCHIQ_MSG_DSTPORT(service->remoteport)), ++ NULL, 0, 0, 0); ++ ++ if (status == VCHIQ_SUCCESS) ++ { ++ if (close_recvd) ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSING); ++ else ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSESENT); ++ } ++ break; ++ ++ case VCHIQ_SRVSTATE_CLOSESENT: ++ vcos_assert(close_recvd); ++ ++ if (!state->is_master) ++ { ++ /* Abort any outstanding bulk transfers */ ++ vcos_mutex_lock(&service->bulk_mutex); ++ abort_outstanding_bulks(service, &service->bulk_tx); ++ abort_outstanding_bulks(service, &service->bulk_rx); ++ status = notify_bulks(service, &service->bulk_tx); ++ if (status == VCHIQ_SUCCESS) ++ status = notify_bulks(service, &service->bulk_rx); ++ vcos_mutex_unlock(&service->bulk_mutex); ++ } ++ ++ if (status == VCHIQ_SUCCESS) ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSING); ++ break; ++ ++ case VCHIQ_SRVSTATE_CLOSING: ++ /* We may come here after a retry */ ++ vcos_assert(!close_recvd); ++ break; ++ ++ default: ++ vcos_log_error("vchiq_close_service_internal(%d) called in state %s", ++ close_recvd, srvstate_names[service->srvstate]); ++ vcos_assert(0); ++ break; ++ } ++ ++ if (service->srvstate == VCHIQ_SRVSTATE_CLOSING) ++ { ++ /* Complete the close process */ ++ vchiq_release_service(&service->base); ++ ++ service->client_id = 0; ++ ++ /* Now tell the client that the services is closed */ ++ if (service->instance) ++ { ++ int oldstate = service->srvstate; ++ ++ /* Change the service state now for the benefit of the callback */ ++ vchiq_set_service_state(service, ++ ((service->public_fourcc == VCHIQ_FOURCC_INVALID) || ++ !service->auto_close) ? ++ VCHIQ_SRVSTATE_CLOSEWAIT : ++ VCHIQ_SRVSTATE_LISTENING); ++ ++ status = make_service_callback(service, VCHIQ_SERVICE_CLOSED, NULL, NULL); ++ ++ if (status == VCHIQ_RETRY) ++ { ++ /* Restore the old state, to be retried later */ ++ vchiq_set_service_state(service, oldstate); ++ } ++ else ++ { ++ if (status == VCHIQ_ERROR) { ++ /* Signal an error (fatal, since the other end will probably have closed) */ ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_OPEN); ++ } ++ } ++ } ++ ++ if (status != VCHIQ_RETRY) ++ { ++ if (service->srvstate == VCHIQ_SRVSTATE_CLOSING) ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_CLOSEWAIT); ++ vcos_event_signal(&service->remove_event); ++ } ++ } ++ ++ return status; ++} ++ ++/* Called from the application process upon process death */ ++void ++vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ ++ vcos_log_info("%d: tsi - (%d<->%d)", state->id, service->localport, service->remoteport); ++ ++ /* Disconnect from the instance, to prevent any callbacks */ ++ service->instance = NULL; ++ ++ /* Mark the service for termination by the slot handler */ ++ request_poll(state, service, VCHIQ_POLL_TERMINATE); ++} ++ ++/* Called from the application process upon process death, and from ++ vchiq_remove_service */ ++void ++vchiq_free_service_internal(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_STATE_T *state = service->state; ++ int slot_last = state->remote->slot_last; ++ int i; ++ ++ vcos_log_info("%d: fsi - (%d)", state->id, service->localport); ++ ++ vcos_mutex_lock(&state->mutex); ++ ++ /* Release any claimed messages */ ++ for (i = state->remote->slot_first; i <= slot_last; i++) ++ { ++ VCHIQ_SLOT_INFO_T *slot_info = SLOT_INFO_FROM_INDEX(state, i); ++ if (slot_info->release_count != slot_info->use_count) ++ { ++ char *data = (char *)SLOT_DATA_FROM_INDEX(state, i); ++ int pos, end; ++ ++ end = VCHIQ_SLOT_SIZE; ++ if (data == state->rx_data) ++ { ++ /* This buffer is still being read from - stop at the current read position */ ++ end = state->rx_pos & VCHIQ_SLOT_MASK; ++ } ++ ++ pos = 0; ++ ++ while (pos < end) ++ { ++ VCHIQ_HEADER_T *header = (VCHIQ_HEADER_T *)(data + pos); ++ int msgid = header->msgid; ++ int port = VCHIQ_MSG_DSTPORT(msgid); ++ if (port == service->localport) ++ { ++ if (msgid & VCHIQ_MSGID_CLAIMED) ++ { ++ header->msgid = msgid & ~VCHIQ_MSGID_CLAIMED; ++ vcos_log_info(" fsi - hdr %x", (unsigned int)header); ++ release_slot(state, slot_info); ++ } ++ } ++ pos += calc_stride(header->size); ++ } ++ } ++ } ++ ++ vcos_assert(state->services[service->localport] == service); ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_FREE); ++ state->services[service->localport] = NULL; ++ vcos_free(service); ++ vcos_mutex_unlock(&state->mutex); ++} ++ ++VCHIQ_STATUS_T ++vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance) ++{ ++ int i; ++ ++ /* Find all services registered to this client and enable them. */ ++ for (i = 0; i < state->unused_service; i++) ++ { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ if (service && (service->instance == instance)) { ++ if (service->srvstate == VCHIQ_SRVSTATE_HIDDEN) ++ vchiq_set_service_state(service, ++ VCHIQ_SRVSTATE_LISTENING); ++ } ++ } ++ ++ if (state->conn_state == VCHIQ_CONNSTATE_DISCONNECTED) { ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_CONNECT, 0, 0), NULL, 0, ++ 0, 1) == VCHIQ_RETRY) ++ return VCHIQ_RETRY; ++ vcos_event_wait(&state->connect); ++ ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); ++ } ++ ++ return VCHIQ_SUCCESS; ++} ++ ++VCHIQ_STATUS_T ++vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ int i; ++ ++ /* Find all services registered to this client and close them. */ ++ for (i = 0; i < state->unused_service; i++) ++ { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ if (service && (service->instance == instance) && ++ ((service->srvstate == VCHIQ_SRVSTATE_OPEN) || ++ (service->srvstate == VCHIQ_SRVSTATE_LISTENING))) ++ { ++ status = vchiq_remove_service(&service->base); ++ if (status != VCHIQ_SUCCESS) ++ break; ++ } ++ } ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_pause_internal(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ switch (state->conn_state) ++ { ++ case VCHIQ_CONNSTATE_CONNECTED: ++ /* Request a pause */ ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_PAUSING); ++ request_poll(state, NULL, 0); ++ break; ++ case VCHIQ_CONNSTATE_PAUSED: ++ break; ++ default: ++ status = VCHIQ_ERROR; ++ VCHIQ_STATS_INC(state, error_count); ++ break; ++ } ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_resume_internal(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ if (state->conn_state == VCHIQ_CONNSTATE_PAUSED) ++ { ++ vchiq_set_conn_state(state, VCHIQ_CONNSTATE_RESUMING); ++ request_poll(state, NULL, 0); ++ } ++ else ++ { ++ status = VCHIQ_ERROR; ++ VCHIQ_STATS_INC(state, error_count); ++ } ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ /* Unregister the service */ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *) handle; ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ ++ if (service == NULL) ++ return VCHIQ_ERROR; ++ ++ vcos_log_info("%d: close_service:%d", service->state->id, service->localport); ++ ++ if (service->public_fourcc != VCHIQ_FOURCC_INVALID) ++ { ++ if (service->srvstate == VCHIQ_SRVSTATE_CLOSEWAIT) ++ { ++ /* This is a non-auto-close server */ ++ vchiq_set_service_state(service, VCHIQ_SRVSTATE_LISTENING); ++ status = VCHIQ_SUCCESS; ++ } ++ } ++ else ++ { ++ /* For clients, make it an alias of vchiq_remove_service */ ++ status = vchiq_remove_service(handle); ++ } ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ /* Unregister the service */ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *) handle; ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ if (service == NULL) ++ return VCHIQ_ERROR; ++ ++ vcos_log_info("%d: remove_service:%d", service->state->id, service->localport); ++ ++ switch (service->srvstate) ++ { ++ case VCHIQ_SRVSTATE_OPENING: ++ case VCHIQ_SRVSTATE_OPEN: ++ /* Mark the service for termination by the slot handler */ ++ request_poll(service->state, service, VCHIQ_POLL_TERMINATE); ++ ++ /* Drop through... */ ++ case VCHIQ_SRVSTATE_CLOSESENT: ++ case VCHIQ_SRVSTATE_CLOSING: ++ while ((service->srvstate != VCHIQ_SRVSTATE_CLOSEWAIT) && ++ (service->srvstate != VCHIQ_SRVSTATE_LISTENING)) ++ { ++ if (vcos_event_wait(&service->remove_event) != VCOS_SUCCESS) { ++ status = VCHIQ_RETRY; ++ break; ++ } ++ } ++ break; ++ ++ default: ++ break; ++ } ++ ++ if (status == VCHIQ_SUCCESS) { ++ if (service->srvstate == VCHIQ_SRVSTATE_OPEN) ++ status = VCHIQ_ERROR; ++ else ++ { ++ service->instance = NULL; ++ vchiq_free_service_internal(service); ++ } ++ } ++ ++ return status; ++} ++ ++ ++VCHIQ_STATUS_T ++vchiq_bulk_transfer(VCHIQ_SERVICE_T *service, ++ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata, ++ VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir) ++{ ++ VCHIQ_BULK_QUEUE_T *queue = (dir == VCHIQ_BULK_TRANSMIT) ? ++ &service->bulk_tx : &service->bulk_rx; ++ VCHIQ_BULK_T *bulk; ++ VCHIQ_STATE_T *state; ++ BULK_WAITER_T bulk_waiter; ++ const char dir_char = (dir == VCHIQ_BULK_TRANSMIT) ? 't' : 'r'; ++ const int dir_msgtype = (dir == VCHIQ_BULK_TRANSMIT) ? VCHIQ_MSG_BULK_TX : VCHIQ_MSG_BULK_RX; ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ ++ if ((service == NULL) || ++ ((memhandle == VCHI_MEM_HANDLE_INVALID) && (offset == NULL))) ++ return VCHIQ_ERROR; ++ ++ state = service->state; ++ ++ if (service->srvstate != VCHIQ_SRVSTATE_OPEN) ++ return VCHIQ_ERROR; /* Must be connected */ ++ ++ if (vcos_mutex_lock(&service->bulk_mutex) != VCOS_SUCCESS) ++ return VCHIQ_RETRY; ++ ++ if (queue->local_insert == queue->remove + VCHIQ_NUM_SERVICE_BULKS) ++ { ++ VCHIQ_SERVICE_STATS_INC(service, bulk_stalls); ++ do { ++ vcos_mutex_unlock(&service->bulk_mutex); ++ if (vcos_event_wait(&service->bulk_remove_event) != VCOS_SUCCESS) ++ return VCHIQ_RETRY; ++ if (vcos_mutex_lock(&service->bulk_mutex) != VCOS_SUCCESS) ++ return VCHIQ_RETRY; ++ } while (queue->local_insert == queue->remove + VCHIQ_NUM_SERVICE_BULKS); ++ } ++ ++ bulk = &queue->bulks[BULK_INDEX(queue->local_insert)]; ++ ++ if (mode == VCHIQ_BULK_MODE_BLOCKING) ++ { ++ vcos_event_create(&bulk_waiter.event, "bulk_waiter"); ++ bulk_waiter.actual = 0; ++ userdata = &bulk_waiter; ++ } ++ ++ bulk->mode = mode; ++ bulk->dir = dir; ++ bulk->userdata = userdata; ++ bulk->size = size; ++ bulk->actual = VCHIQ_BULK_ACTUAL_ABORTED; ++ ++ if (vchiq_prepare_bulk_data(bulk, memhandle, offset, size, dir) != VCHIQ_SUCCESS) ++ { ++ goto error_exit; ++ } ++ ++ vcos_log_info("%d: bt (%d->%d) %cx %x@%x %x", state->id, ++ service->localport, service->remoteport, dir_char, ++ size, (unsigned int)bulk->data, (unsigned int)userdata); ++ ++ if (state->is_master) ++ { ++ queue->local_insert++; ++ if (resolve_bulks(service, queue)) ++ request_poll(state, service, (dir == VCHIQ_BULK_TRANSMIT) ? ++ VCHIQ_POLL_TXNOTIFY : VCHIQ_POLL_RXNOTIFY); ++ } ++ else ++ { ++ int payload[2] = { (int)bulk->data, bulk->size }; ++ VCHIQ_ELEMENT_T element = { payload, sizeof(payload) }; ++ ++ if (queue_message(state, NULL, ++ VCHIQ_MAKE_MSG(dir_msgtype, ++ service->localport, service->remoteport), ++ &element, 1, sizeof(payload), 1) != VCHIQ_SUCCESS) ++ { ++ vchiq_complete_bulk(bulk); ++ goto error_exit; ++ } ++ queue->local_insert++; ++ queue->remote_insert++; ++ } ++ ++ vcos_mutex_unlock(&service->bulk_mutex); ++ ++ vcos_log_trace("%d: bt:%d %cx li=%x ri=%x p=%x", state->id, ++ service->localport, dir_char, ++ queue->local_insert, queue->remote_insert, queue->process); ++ ++ status = VCHIQ_SUCCESS; ++ ++ if (mode == VCHIQ_BULK_MODE_BLOCKING) ++ { ++ if (vcos_event_wait(&bulk_waiter.event) != VCOS_SUCCESS) ++ { ++ vcos_log_info("bulk wait interrupted"); ++ /* Stop notify_bulks signalling a non-existent waiter */ ++ bulk->userdata = NULL; ++ status = VCHIQ_ERROR; ++ } ++ else if (bulk_waiter.actual == VCHIQ_BULK_ACTUAL_ABORTED) ++ status = VCHIQ_ERROR; ++ ++ vcos_event_delete(&bulk_waiter.event); ++ } ++ ++ return status; ++ ++error_exit: ++ if (mode == VCHIQ_BULK_MODE_BLOCKING) ++ vcos_event_delete(&bulk_waiter.event); ++ vcos_mutex_unlock(&service->bulk_mutex); ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, ++ const void *data, int size, void *userdata) ++{ ++ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle, ++ VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, ++ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT); ++} ++ ++VCHIQ_STATUS_T ++vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, int size, ++ void *userdata) ++{ ++ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle, ++ VCHI_MEM_HANDLE_INVALID, data, size, userdata, ++ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE); ++} ++ ++VCHIQ_STATUS_T ++vchiq_queue_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T memhandle, const void *offset, int size, void *userdata) ++{ ++ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle, ++ memhandle, (void *)offset, size, userdata, ++ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_TRANSMIT); ++} ++ ++VCHIQ_STATUS_T ++vchiq_queue_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata) ++{ ++ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle, ++ memhandle, offset, size, userdata, ++ VCHIQ_BULK_MODE_CALLBACK, VCHIQ_BULK_RECEIVE); ++} ++ ++VCHIQ_STATUS_T ++vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, const void *data, int size, ++ void *userdata, VCHIQ_BULK_MODE_T mode) ++{ ++ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle, ++ VCHI_MEM_HANDLE_INVALID, (void *)data, size, userdata, ++ mode, VCHIQ_BULK_TRANSMIT); ++} ++ ++VCHIQ_STATUS_T ++vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, void *data, int size, ++ void *userdata, VCHIQ_BULK_MODE_T mode) ++{ ++ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle, ++ VCHI_MEM_HANDLE_INVALID, data, size, userdata, ++ mode, VCHIQ_BULK_RECEIVE); ++} ++ ++VCHIQ_STATUS_T ++vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T memhandle, const void *offset, int size, void *userdata, ++ VCHIQ_BULK_MODE_T mode) ++{ ++ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle, ++ memhandle, (void *)offset, size, userdata, ++ mode, VCHIQ_BULK_TRANSMIT); ++} ++ ++VCHIQ_STATUS_T ++vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata, ++ VCHIQ_BULK_MODE_T mode) ++{ ++ return vchiq_bulk_transfer((VCHIQ_SERVICE_T *)handle, ++ memhandle, offset, size, userdata, ++ mode, VCHIQ_BULK_RECEIVE); ++} ++ ++VCHIQ_STATUS_T ++vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle, ++ const VCHIQ_ELEMENT_T *elements, int count) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *) handle; ++ ++ unsigned int size = 0; ++ unsigned int i; ++ ++ if ((service == NULL) || ++ (service->srvstate != VCHIQ_SRVSTATE_OPEN)) ++ return VCHIQ_ERROR; ++ ++ for (i = 0; i < (unsigned int)count; i++) ++ { ++ if (elements[i].size) ++ { ++ if (elements[i].data == NULL) ++ { ++ VCHIQ_SERVICE_STATS_INC(service, error_count); ++ return VCHIQ_ERROR; ++ } ++ size += elements[i].size; ++ } ++ } ++ ++ if (size > VCHIQ_MAX_MSG_SIZE) ++ { ++ VCHIQ_SERVICE_STATS_INC(service, error_count); ++ return VCHIQ_ERROR; ++ } ++ ++ return queue_message(service->state, service, ++ VCHIQ_MAKE_MSG(VCHIQ_MSG_DATA, service->localport, ++ service->remoteport), elements, count, size, 1); ++} ++ ++void ++vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle, VCHIQ_HEADER_T *header) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ VCHIQ_STATE_T *state; ++ int slot_index; ++ int msgid; ++ ++ if (service == NULL) ++ return; ++ ++ state = service->state; ++ ++ slot_index = SLOT_INDEX_FROM_DATA(state, (void *)header); ++ ++ if ((slot_index >= state->remote->slot_first) && ++ (slot_index <= state->remote->slot_last) && ++ ((msgid = header->msgid) & VCHIQ_MSGID_CLAIMED)) ++ { ++ VCHIQ_SLOT_INFO_T *slot_info = SLOT_INFO_FROM_INDEX(state, slot_index); ++ ++ /* Rewrite the message header to prevent a double release */ ++ header->msgid = msgid & ~VCHIQ_MSGID_CLAIMED; ++ ++ release_slot(state, slot_info); ++ } ++} ++ ++int ++vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ return service ? service->client_id : 0; ++} ++ ++VCHIQ_STATUS_T ++vchiq_get_config(VCHIQ_INSTANCE_T instance, ++ int config_size, VCHIQ_CONFIG_T *pconfig) ++{ ++ VCHIQ_CONFIG_T config; ++ ++ vcos_unused(instance); ++ ++ config.max_msg_size = VCHIQ_MAX_MSG_SIZE; ++ config.bulk_threshold = VCHIQ_MAX_MSG_SIZE; ++ config.max_outstanding_bulks = VCHIQ_NUM_SERVICE_BULKS; ++ config.max_services = VCHIQ_MAX_SERVICES; ++ config.version = VCHIQ_VERSION; ++ config.version_min = VCHIQ_VERSION_MIN; ++ ++ if (config_size > sizeof(VCHIQ_CONFIG_T)) ++ return VCHIQ_ERROR; ++ ++ memcpy(pconfig, &config, vcos_min(config_size, sizeof(VCHIQ_CONFIG_T))); ++ ++ return VCHIQ_SUCCESS; ++} ++ ++VCHIQ_STATUS_T ++vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHIQ_SERVICE_OPTION_T option, int value) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ ++ if (service) ++ { ++ switch (option) ++ { ++ case VCHIQ_SERVICE_OPTION_AUTOCLOSE: ++ service->auto_close = value; ++ status = VCHIQ_SUCCESS; ++ break; ++ ++ default: ++ break; ++ } ++ } ++ ++ return status; ++} ++ ++void ++vchiq_dump_shared_state(void *dump_context, VCHIQ_STATE_T *state, ++ VCHIQ_SHARED_STATE_T *shared, const char *label) ++{ ++ static const char *const debug_names[] = ++ { ++ "<entries>", ++ "SLOT_HANDLER_COUNT", ++ "SLOT_HANDLER_LINE", ++ "PARSE_LINE", ++ "PARSE_HEADER", ++ "PARSE_MSGID", ++ "AWAIT_COMPLETION_LINE", ++ "DEQUEUE_MESSAGE_LINE", ++ "SERVICE_CALLBACK_LINE", ++ "MSG_QUEUE_FULL_COUNT", ++ "COMPLETION_QUEUE_FULL_COUNT" ++ }; ++ int i; ++ ++ char buf[80]; ++ int len; ++ len = vcos_snprintf(buf, sizeof(buf), ++ " %s: slots %d-%d tx_pos=%x recycle=%x", ++ label, shared->slot_first, shared->slot_last, ++ shared->tx_pos, shared->slot_queue_recycle); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = vcos_snprintf(buf, sizeof(buf), ++ " Slots claimed:"); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ for (i = shared->slot_first; i <= shared->slot_last; i++) ++ { ++ VCHIQ_SLOT_INFO_T slot_info = *SLOT_INFO_FROM_INDEX(state, i); ++ if (slot_info.use_count != slot_info.release_count) ++ { ++ len = vcos_snprintf(buf, sizeof(buf), ++ " %d: %d/%d", i, slot_info.use_count, slot_info.release_count); ++ vchiq_dump(dump_context, buf, len + 1); ++ } ++ } ++ ++ for (i = 1; i < shared->debug[DEBUG_ENTRIES]; i++) ++ { ++ len = vcos_snprintf(buf, sizeof(buf), " DEBUG: %s = %d(%x)", ++ debug_names[i], shared->debug[i], shared->debug[i]); ++ vchiq_dump(dump_context, buf, len + 1); ++ } ++} ++ ++void ++vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state) ++{ ++ char buf[80]; ++ int len; ++ int i; ++ ++ len = vcos_snprintf(buf, sizeof(buf), "State %d: %s", state->id, ++ conn_state_names[state->conn_state]); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = vcos_snprintf(buf, sizeof(buf), ++ " tx_pos=%x(@%x), rx_pos=%x(@%x)", ++ state->id, state->local->tx_pos, ++ (uint32_t)state->tx_data + (state->local_tx_pos & VCHIQ_SLOT_MASK), ++ state->rx_pos, ++ (uint32_t)state->rx_data + (state->rx_pos & VCHIQ_SLOT_MASK)); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = vcos_snprintf(buf, sizeof(buf), ++ " Version: %d (min %d)", ++ VCHIQ_VERSION, VCHIQ_VERSION_MIN); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ if (VCHIQ_ENABLE_STATS) ++ { ++ len = vcos_snprintf(buf, sizeof(buf), ++ " Stats: ctrl_tx_count=%d, ctrl_rx_count=%d, error_count=%d", ++ state->stats.ctrl_tx_count, state->stats.ctrl_rx_count, ++ state->stats.slot_stalls); ++ vchiq_dump(dump_context, buf, len + 1); ++ } ++ ++ len = vcos_snprintf(buf, sizeof(buf), ++ " Slots: %d available, %d recyclable, %d stalls", ++ state->slot_queue_available - SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos), ++ state->local->slot_queue_recycle - state->slot_queue_available, ++ state->stats.slot_stalls); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ vchiq_dump_platform_state(dump_context); ++ ++ vchiq_dump_shared_state(dump_context, state, state->local, "Local"); ++ vchiq_dump_shared_state(dump_context, state, state->remote, "Remote"); ++ ++ vchiq_dump_platform_instances(dump_context); ++ ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service = state->services[i]; ++ ++ if (service && (service->srvstate != VCHIQ_SRVSTATE_FREE)) ++ vchiq_dump_service_state(dump_context, service); ++ } ++} ++ ++void ++vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service) ++{ ++ char buf[80]; ++ int len; ++ ++ len = vcos_snprintf(buf, sizeof(buf), "Service %d: %s", ++ service->localport, srvstate_names[service->srvstate]); ++ ++ if (service->srvstate != VCHIQ_SRVSTATE_FREE) ++ { ++ char remoteport[30]; ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &service->state->service_quotas[service->localport]; ++ int fourcc = service->base.fourcc; ++ if (service->remoteport != VCHIQ_PORT_FREE) ++ { ++ int len2 = vcos_snprintf(remoteport, sizeof(remoteport), "%d", ++ service->remoteport); ++ if (service->public_fourcc != VCHIQ_FOURCC_INVALID) ++ vcos_snprintf(remoteport + len2, sizeof(remoteport) - len2, ++ " (client %x)", service->client_id); ++ } ++ else ++ vcos_strcpy(remoteport, "n/a"); ++ ++ len += vcos_snprintf(buf + len, sizeof(buf) - len, ++ " '%c%c%c%c' remote %s (slot use %d/%d)", ++ VCHIQ_FOURCC_AS_4CHARS(fourcc), ++ remoteport, ++ service_quota->slot_use_count, ++ service_quota->slot_quota); ++ ++ if (VCHIQ_ENABLE_STATS) ++ { ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = vcos_snprintf(buf, sizeof(buf), ++ " Ctrl: tx_count=%d, tx_bytes=%" PRIu64 ", rx_count=%d, rx_bytes=%" PRIu64, ++ service->stats.ctrl_tx_count, service->stats.ctrl_tx_bytes, ++ service->stats.ctrl_rx_count, service->stats.ctrl_rx_bytes); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = vcos_snprintf(buf, sizeof(buf), ++ " Bulk: tx_count=%d, tx_bytes=%" PRIu64 ", rx_count=%d, rx_bytes=%" PRIu64, ++ service->stats.bulk_tx_count, service->stats.bulk_tx_bytes, ++ service->stats.bulk_rx_count, service->stats.bulk_rx_bytes); ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ len = vcos_snprintf(buf, sizeof(buf), ++ " %d quota stalls, %d slot stalls, %d bulk stalls, %d aborted, %d errors", ++ service->stats.quota_stalls, service->stats.slot_stalls, ++ service->stats.bulk_stalls, service->stats.bulk_aborted_count, ++ service->stats.error_count); ++ } ++ } ++ ++ vchiq_dump(dump_context, buf, len + 1); ++ ++ vchiq_dump_platform_service_state(dump_context, service); ++} +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h +@@ -0,0 +1,480 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef VCHIQ_CORE_H ++#define VCHIQ_CORE_H ++ ++#include "vchiq_cfg.h" ++ ++#include "vchiq.h" ++ ++#define IS_POW2(x) (x && ((x & (x - 1)) == 0)) ++ ++/* Ensure that the slot size and maximum number of slots are powers of 2 */ ++vcos_static_assert(IS_POW2(VCHIQ_SLOT_SIZE)); ++vcos_static_assert(IS_POW2(VCHIQ_MAX_SLOTS)); ++vcos_static_assert(IS_POW2(VCHIQ_MAX_SLOTS_PER_SIDE)); ++ ++#define VCHIQ_SLOT_MASK (VCHIQ_SLOT_SIZE - 1) ++#define VCHIQ_SLOT_QUEUE_MASK (VCHIQ_MAX_SLOTS_PER_SIDE - 1) ++#define VCHIQ_SLOT_ZERO_SLOTS ((sizeof(VCHIQ_SLOT_ZERO_T) + \ ++ VCHIQ_SLOT_SIZE - 1) / VCHIQ_SLOT_SIZE) ++ ++#define VCHIQ_MSG_PADDING 0 // - ++#define VCHIQ_MSG_CONNECT 1 // - ++#define VCHIQ_MSG_OPEN 2 // + (srcport, -), fourcc, client_id ++#define VCHIQ_MSG_OPENACK 3 // + (srcport, dstport) ++#define VCHIQ_MSG_CLOSE 4 // + (srcport, dstport) ++#define VCHIQ_MSG_DATA 5 // + (srcport, dstport) ++#define VCHIQ_MSG_BULK_RX 6 // + (srcport, dstport), data, size ++#define VCHIQ_MSG_BULK_TX 7 // + (srcport, dstport), data, size ++#define VCHIQ_MSG_BULK_RX_DONE 8 // + (srcport, dstport), actual ++#define VCHIQ_MSG_BULK_TX_DONE 9 // + (srcport, dstport), actual ++#define VCHIQ_MSG_PAUSE 10 // - ++#define VCHIQ_MSG_RESUME 11 // - ++ ++#define VCHIQ_PORT_MAX (VCHIQ_MAX_SERVICES - 1) ++#define VCHIQ_PORT_FREE 0x1000 ++#define VCHIQ_PORT_IS_VALID(port) (port < VCHIQ_PORT_FREE) ++#define VCHIQ_MAKE_MSG(type,srcport,dstport) ((type<<24) | (srcport<<12) | (dstport<<0)) ++#define VCHIQ_MSG_TYPE(msgid) ((unsigned int)msgid >> 24) ++#define VCHIQ_MSG_SRCPORT(msgid) (unsigned short)(((unsigned int)msgid >> 12) & 0xfff) ++#define VCHIQ_MSG_DSTPORT(msgid) ((unsigned short)msgid & 0xfff) ++ ++#define VCHIQ_FOURCC_AS_4CHARS(fourcc) \ ++ ((fourcc) >> 24) & 0xff, \ ++ ((fourcc) >> 16) & 0xff, \ ++ ((fourcc) >> 8) & 0xff, \ ++ ((fourcc) ) & 0xff ++ ++/* Ensure the fields are wide enough */ ++vcos_static_assert(VCHIQ_MSG_SRCPORT(VCHIQ_MAKE_MSG(0,0,VCHIQ_PORT_MAX)) == 0); ++vcos_static_assert(VCHIQ_MSG_TYPE(VCHIQ_MAKE_MSG(0,VCHIQ_PORT_MAX,0)) == 0); ++vcos_static_assert((unsigned int)VCHIQ_PORT_MAX < (unsigned int)VCHIQ_PORT_FREE); ++ ++#define VCHIQ_MSGID_PADDING VCHIQ_MAKE_MSG(VCHIQ_MSG_PADDING,0,0) ++#define VCHIQ_MSGID_CLAIMED 0x40000000 ++ ++#define VCHIQ_FOURCC_INVALID 0x00000000 ++#define VCHIQ_FOURCC_IS_LEGAL(fourcc) (fourcc != VCHIQ_FOURCC_INVALID) ++ ++#define VCHIQ_BULK_ACTUAL_ABORTED -1 ++ ++typedef uint32_t BITSET_T; ++ ++vcos_static_assert((sizeof(BITSET_T) * 8) == 32); ++ ++#define BITSET_SIZE(b) ((b + 31) >> 5) ++#define BITSET_WORD(b) (b >> 5) ++#define BITSET_BIT(b) (1 << (b & 31)) ++#define BITSET_ZERO(bs) memset(bs, 0, sizeof(bs)) ++#define BITSET_IS_SET(bs, b) (bs[BITSET_WORD(b)] & BITSET_BIT(b)) ++#define BITSET_SET(bs, b) (bs[BITSET_WORD(b)] |= BITSET_BIT(b)) ++#define BITSET_CLR(bs, b) (bs[BITSET_WORD(b)] &= ~BITSET_BIT(b)) ++ ++#if VCHIQ_ENABLE_STATS ++#define VCHIQ_STATS_INC(state, stat) (state->stats. stat ++) ++#define VCHIQ_SERVICE_STATS_INC(service, stat) (service->stats. stat ++) ++#define VCHIQ_SERVICE_STATS_ADD(service, stat, addend) (service->stats. stat += addend) ++#else ++#define VCHIQ_STATS_INC(state, stat) ((void)0) ++#define VCHIQ_SERVICE_STATS_INC(service, stat) ((void)0) ++#define VCHIQ_SERVICE_STATS_ADD(service, stat, addend) ((void)0) ++#endif ++ ++enum ++{ ++ DEBUG_ENTRIES, ++#if VCHIQ_ENABLE_DEBUG ++ DEBUG_SLOT_HANDLER_COUNT, ++ DEBUG_SLOT_HANDLER_LINE, ++ DEBUG_PARSE_LINE, ++ DEBUG_PARSE_HEADER, ++ DEBUG_PARSE_MSGID, ++ DEBUG_AWAIT_COMPLETION_LINE, ++ DEBUG_DEQUEUE_MESSAGE_LINE, ++ DEBUG_SERVICE_CALLBACK_LINE, ++ DEBUG_MSG_QUEUE_FULL_COUNT, ++ DEBUG_COMPLETION_QUEUE_FULL_COUNT, ++#endif ++ DEBUG_MAX ++}; ++ ++#if VCHIQ_ENABLE_DEBUG ++ ++#define DEBUG_INITIALISE(local) volatile int *debug_ptr = (local)->debug; ++#define DEBUG_TRACE(d) debug_ptr[DEBUG_ ## d] = __LINE__ ++#define DEBUG_VALUE(d,v) debug_ptr[DEBUG_ ## d] = (v) ++#define DEBUG_COUNT(d) debug_ptr[DEBUG_ ## d]++ ++ ++#else /* VCHIQ_ENABLE_DEBUG */ ++ ++#define DEBUG_INITIALISE(local) ++#define DEBUG_TRACE(d) ++#define DEBUG_VALUE(d,v) ++#define DEBUG_COUNT(d) ++ ++#endif /* VCHIQ_ENABLE_DEBUG */ ++ ++typedef enum ++{ ++ VCHIQ_CONNSTATE_DISCONNECTED, ++ VCHIQ_CONNSTATE_CONNECTED, ++ VCHIQ_CONNSTATE_PAUSING, ++ VCHIQ_CONNSTATE_PAUSE_SENT, ++ VCHIQ_CONNSTATE_PAUSED, ++ VCHIQ_CONNSTATE_RESUMING ++} VCHIQ_CONNSTATE_T; ++ ++enum ++{ ++ VCHIQ_SRVSTATE_FREE, ++ VCHIQ_SRVSTATE_HIDDEN, ++ VCHIQ_SRVSTATE_LISTENING, ++ VCHIQ_SRVSTATE_OPENING, ++ VCHIQ_SRVSTATE_OPEN, ++ VCHIQ_SRVSTATE_CLOSESENT, ++ VCHIQ_SRVSTATE_CLOSING, ++ VCHIQ_SRVSTATE_CLOSEWAIT ++}; ++ ++enum ++{ ++ VCHIQ_POLL_TERMINATE, ++ VCHIQ_POLL_TXNOTIFY, ++ VCHIQ_POLL_RXNOTIFY, ++ VCHIQ_POLL_COUNT ++}; ++ ++typedef enum ++{ ++ VCHIQ_BULK_TRANSMIT, ++ VCHIQ_BULK_RECEIVE ++} VCHIQ_BULK_DIR_T; ++ ++typedef struct vchiq_bulk_struct { ++ short mode; ++ short dir; ++ void *userdata; ++ VCHI_MEM_HANDLE_T handle; ++ void *data; ++ int size; ++ void *remote_data; ++ int remote_size; ++ int actual; ++} VCHIQ_BULK_T; ++ ++typedef struct vchiq_bulk_queue_struct { ++ int local_insert; /* Where to insert the next local bulk */ ++ int remote_insert; /* Where to insert the next remote bulk (master) */ ++ int process; /* Bulk to transfer next */ ++ int remote_notify; /* Bulk to notify the remote client of next (master) */ ++ int remove; /* Bulk to notify the local client of, and remove, next */ ++ VCHIQ_BULK_T bulks[VCHIQ_NUM_SERVICE_BULKS]; ++} VCHIQ_BULK_QUEUE_T; ++ ++typedef struct remote_event_struct { ++ volatile int armed; ++ volatile int fired; ++ VCOS_EVENT_T * event; ++} REMOTE_EVENT_T; ++ ++typedef struct vchiq_state_struct VCHIQ_STATE_T; ++ ++typedef struct vchiq_slot_struct { ++ char data[VCHIQ_SLOT_SIZE]; ++} VCHIQ_SLOT_T; ++ ++typedef struct vchiq_slot_info_struct { ++ /* Use two counters rather than one to avoid the need for a mutex. */ ++ volatile short use_count; ++ volatile short release_count; ++} VCHIQ_SLOT_INFO_T; ++ ++typedef struct vchiq_service_struct { ++ VCHIQ_SERVICE_BASE_T base; ++ volatile int srvstate; ++ unsigned int localport; ++ unsigned int remoteport; ++ int public_fourcc; ++ int client_id; ++ int auto_close; ++ VCOS_ATOMIC_FLAGS_T poll_flags; ++ short version; ++ short version_min; ++ ++ VCHIQ_STATE_T *state; ++ VCHIQ_INSTANCE_T instance; ++ ++ int service_use_count; ++ ++ VCHIQ_BULK_QUEUE_T bulk_tx; ++ VCHIQ_BULK_QUEUE_T bulk_rx; ++ ++ VCOS_EVENT_T remove_event; ++ VCOS_EVENT_T bulk_remove_event; ++ VCOS_MUTEX_T bulk_mutex; ++ ++ struct service_stats_struct ++ { ++ int quota_stalls; ++ int slot_stalls; ++ int bulk_stalls; ++ int error_count; ++ int ctrl_tx_count; ++ int ctrl_rx_count; ++ int bulk_tx_count; ++ int bulk_rx_count; ++ int bulk_aborted_count; ++ uint64_t ctrl_tx_bytes; ++ uint64_t ctrl_rx_bytes; ++ uint64_t bulk_tx_bytes; ++ uint64_t bulk_rx_bytes; ++ } stats; ++} VCHIQ_SERVICE_T; ++ ++/* The quota information is outside VCHIQ_SERVICE_T so that it can be ++ statically allocated, since for accounting reasons a service's slot ++ usage is carried over between users of the same port number. ++ */ ++typedef struct vchiq_service_quota_struct { ++ int slot_quota; ++ int slot_use_count; ++ VCOS_EVENT_T quota_event; ++ int previous_tx_index; ++} VCHIQ_SERVICE_QUOTA_T; ++ ++typedef struct vchiq_shared_state_struct { ++ ++ /* A non-zero value here indicates that the content is valid. */ ++ int initialised; ++ ++ /* The first and last (inclusive) slots allocated to the owner. */ ++ int slot_first; ++ int slot_last; ++ ++ /* Signalling this event indicates that owner's slot handler thread should ++ run. */ ++ REMOTE_EVENT_T trigger; ++ ++ /* Indicates the byte position within the stream where the next message ++ will be written. The least significant bits are an index into the slot. ++ The next bits are the index of the slot in slot_queue. */ ++ volatile int tx_pos; ++ ++ /* This event should be signalled when a slot is recycled. */ ++ REMOTE_EVENT_T recycle; ++ ++ /* The slot_queue index where the next recycled slot will be written. */ ++ volatile int slot_queue_recycle; ++ ++ /* A circular buffer of slot indexes. */ ++ int slot_queue[VCHIQ_MAX_SLOTS_PER_SIDE]; ++ ++ /* Debugging state */ ++ volatile int debug[DEBUG_MAX]; ++} VCHIQ_SHARED_STATE_T; ++ ++typedef struct vchiq_slot_zero_struct { ++ int magic; ++ short version; ++ short version_min; ++ int slot_zero_size; ++ int slot_size; ++ int max_slots; ++ int max_slots_per_side; ++ int platform_data[2]; ++ VCHIQ_SHARED_STATE_T master; ++ VCHIQ_SHARED_STATE_T slave; ++ VCHIQ_SLOT_INFO_T slots[VCHIQ_MAX_SLOTS]; ++} VCHIQ_SLOT_ZERO_T; ++ ++struct vchiq_state_struct { ++ int id; ++ int initialised; ++ VCHIQ_CONNSTATE_T conn_state; ++ int is_master; ++ ++ VCHIQ_SHARED_STATE_T *local; ++ VCHIQ_SHARED_STATE_T *remote; ++ VCHIQ_SLOT_T *slot_data; ++ ++ int default_slot_quota; ++ ++ VCOS_EVENT_T connect; // event indicating connect message received ++ VCOS_MUTEX_T mutex; // mutex protecting services ++ VCHIQ_INSTANCE_T *instance; ++ ++ VCOS_THREAD_T slot_handler_thread; // processes incoming messages ++ VCOS_THREAD_T recycle_thread; // processes recycled slots ++ VCOS_THREAD_T lp_thread; // processes low priority messages (eg suspend) ++ ++ /* Local implementation of the trigger remote event */ ++ VCOS_EVENT_T trigger_event; ++ ++ /* Local implementation of the recycle remote event */ ++ VCOS_EVENT_T recycle_event; ++ ++ VCOS_EVENT_T lp_evt; ++ ++ char *tx_data; ++ char *rx_data; ++ VCHIQ_SLOT_INFO_T *rx_info; ++ ++ VCOS_MUTEX_T slot_mutex; ++ ++ VCOS_MUTEX_T recycle_mutex; ++ ++ VCOS_MUTEX_T suspend_resume_mutex; ++ VCOS_MUTEX_T use_count_mutex; ++ ++ /* Global use count for videocore. ++ * This is equal to the sum of the use counts for all services. When this hits ++ * zero the videocore suspend procedure will be initiated. */ ++ int videocore_use_count; ++ ++ /* Flag to indicate whether videocore is currently suspended */ ++ int videocore_suspended; ++ ++ /* Indicates the byte position within the stream from where the next message ++ will be read. The least significant bits are an index into the slot. ++ The next bits are the index of the slot in remote->slot_queue. */ ++ int rx_pos; ++ ++ /* A cached copy of local->tx_pos. Only write to local->tx_pos, and read ++ from remote->tx_pos. */ ++ int local_tx_pos; ++ ++ /* The slot_queue index of the slot to become available next. */ ++ int slot_queue_available; ++ ++ /* A flag to indicate if any poll has been requested */ ++ int poll_needed; ++ ++ /* An array of bit sets indicating which services must be polled. */ ++ VCOS_ATOMIC_FLAGS_T poll_services[BITSET_SIZE(VCHIQ_MAX_SERVICES)]; ++ ++ /* The number of the first unused service */ ++ int unused_service; ++ ++ /* Signalled when a free slot becomes available. */ ++ VCOS_EVENT_T slot_available_event; ++ ++ VCOS_EVENT_T slot_remove_event; ++ ++ struct state_stats_struct ++ { ++ int slot_stalls; ++ int ctrl_tx_count; ++ int ctrl_rx_count; ++ int error_count; ++ } stats; ++ ++ VCHIQ_SERVICE_T *services[VCHIQ_MAX_SERVICES]; ++ VCHIQ_SERVICE_QUOTA_T service_quotas[VCHIQ_MAX_SERVICES]; ++ VCHIQ_SLOT_INFO_T slot_info[VCHIQ_MAX_SLOTS]; ++}; ++ ++extern VCHIQ_SLOT_ZERO_T * ++vchiq_init_slots(void *mem_base, int mem_size); ++ ++extern VCHIQ_STATUS_T ++vchiq_init_state(VCHIQ_STATE_T *state, VCHIQ_SLOT_ZERO_T *slot_zero, int is_master); ++ ++extern VCHIQ_STATUS_T ++vchiq_connect_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance); ++ ++extern VCHIQ_SERVICE_T * ++vchiq_add_service_internal(VCHIQ_STATE_T *state, ++ const VCHIQ_SERVICE_PARAMS_T *params, int srvstate, ++ VCHIQ_INSTANCE_T instance); ++ ++extern VCHIQ_STATUS_T ++vchiq_open_service_internal(VCHIQ_SERVICE_T *service, int client_id); ++ ++extern VCHIQ_STATUS_T ++vchiq_close_service_internal(VCHIQ_SERVICE_T *service, int close_recvd); ++ ++extern void ++vchiq_terminate_service_internal(VCHIQ_SERVICE_T *service); ++ ++extern void ++vchiq_free_service_internal(VCHIQ_SERVICE_T *service); ++ ++extern VCHIQ_STATUS_T ++vchiq_shutdown_internal(VCHIQ_STATE_T *state, VCHIQ_INSTANCE_T instance); ++ ++extern VCHIQ_STATUS_T ++vchiq_pause_internal(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_resume_internal(VCHIQ_STATE_T *state); ++ ++extern void ++remote_event_pollall(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_bulk_transfer(VCHIQ_SERVICE_T *service, ++ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, void *userdata, ++ VCHIQ_BULK_MODE_T mode, VCHIQ_BULK_DIR_T dir); ++ ++extern void ++vchiq_dump_state(void *dump_context, VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_dump_service_state(void *dump_context, VCHIQ_SERVICE_T *service); ++ ++/* The following functions are called from vchiq_core, and external ++ implementations must be provided. */ ++ ++extern VCHIQ_STATUS_T ++vchiq_prepare_bulk_data(VCHIQ_BULK_T *bulk, ++ VCHI_MEM_HANDLE_T memhandle, void *offset, int size, int dir); ++ ++extern void ++vchiq_transfer_bulk(VCHIQ_BULK_T *bulk); ++ ++extern void ++vchiq_complete_bulk(VCHIQ_BULK_T *bulk); ++ ++extern VCHIQ_STATUS_T ++vchiq_copy_from_user(void *dst, const void *src, int size); ++ ++extern void ++remote_event_signal(REMOTE_EVENT_T *event); ++ ++extern void ++vchiq_platform_paused(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_platform_resumed(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_dump(void *dump_context, const char *str, int len); ++ ++extern void ++vchiq_dump_platform_state(void *dump_context); ++ ++extern void ++vchiq_dump_platform_instances(void *dump_context); ++ ++extern void ++vchiq_dump_platform_service_state(void *dump_context, ++ VCHIQ_SERVICE_T *service); ++ ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h +@@ -0,0 +1,148 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef VCHIQ_IF_H ++#define VCHIQ_IF_H ++ ++#include "interface/vchi/vchi_mh.h" ++ ++#define VCHIQ_SLOT_SIZE 4096 ++#define VCHIQ_MAX_MSG_SIZE (VCHIQ_SLOT_SIZE - sizeof(VCHIQ_HEADER_T)) ++#define VCHIQ_CHANNEL_SIZE VCHIQ_MAX_MSG_SIZE /* For backwards compatibility */ ++ ++#define VCHIQ_MAKE_FOURCC(x0, x1, x2, x3) (((x0) << 24) | ((x1) << 16) | ((x2) << 8) | (x3)) ++#define VCHIQ_GET_SERVICE_USERDATA(service) (service->userdata) ++#define VCHIQ_GET_SERVICE_FOURCC(service) (service->fourcc) ++ ++typedef enum { ++ VCHIQ_SERVICE_OPENED, // service, -, - ++ VCHIQ_SERVICE_CLOSED, // service, -, - ++ VCHIQ_MESSAGE_AVAILABLE, // service, header, - ++ VCHIQ_BULK_TRANSMIT_DONE, // service, -, bulk_userdata ++ VCHIQ_BULK_RECEIVE_DONE, // service, -, bulk_userdata ++ VCHIQ_BULK_TRANSMIT_ABORTED, // service, -, bulk_userdata ++ VCHIQ_BULK_RECEIVE_ABORTED // service, -, bulk_userdata ++} VCHIQ_REASON_T; ++ ++typedef enum ++{ ++ VCHIQ_ERROR = -1, ++ VCHIQ_SUCCESS = 0, ++ VCHIQ_RETRY = 1 ++} VCHIQ_STATUS_T; ++ ++typedef enum ++{ ++ VCHIQ_BULK_MODE_CALLBACK, ++ VCHIQ_BULK_MODE_BLOCKING, ++ VCHIQ_BULK_MODE_NOCALLBACK ++} VCHIQ_BULK_MODE_T; ++ ++typedef enum ++{ ++ VCHIQ_SERVICE_OPTION_AUTOCLOSE ++} VCHIQ_SERVICE_OPTION_T; ++ ++#ifdef __HIGHC__ ++/* Allow zero-sized arrays without warnings */ ++#pragma warning (push) ++#pragma warning (disable : 4200) ++#endif ++ ++typedef struct vchiq_header_struct { ++ /* The message identifier - opaque to applications. */ ++ int msgid; ++ ++ /* Size of message data. */ ++ unsigned int size; ++ ++ char data[0]; /* message */ ++} VCHIQ_HEADER_T; ++ ++#ifdef __HIGHC__ ++#pragma warning (pop) ++#endif ++ ++typedef struct { ++ const void *data; ++ int size; ++} VCHIQ_ELEMENT_T; ++ ++typedef const struct vchiq_service_base_struct *VCHIQ_SERVICE_HANDLE_T; ++ ++typedef VCHIQ_STATUS_T (*VCHIQ_CALLBACK_T)(VCHIQ_REASON_T, VCHIQ_HEADER_T *, VCHIQ_SERVICE_HANDLE_T, void *); ++ ++typedef struct vchiq_service_base_struct { ++ int fourcc; ++ VCHIQ_CALLBACK_T callback; ++ void *userdata; ++} VCHIQ_SERVICE_BASE_T; ++ ++typedef struct vchiq_service_params_struct { ++ int fourcc; ++ VCHIQ_CALLBACK_T callback; ++ void *userdata; ++ short version; /* Increment for non-trivial changes */ ++ short version_min; /* Update for incompatible changes */ ++} VCHIQ_SERVICE_PARAMS_T; ++ ++typedef struct vchiq_config_struct { ++ int max_msg_size; ++ int bulk_threshold; /* The message size aboce which it is better to use ++ a bulk transfer (<= max_msg_size) */ ++ int max_outstanding_bulks; ++ int max_services; ++ short version; /* The version of VCHIQ */ ++ short version_min; /* The minimum compatible version of VCHIQ */ ++} VCHIQ_CONFIG_T; ++ ++typedef struct vchiq_instance_struct *VCHIQ_INSTANCE_T; ++ ++extern VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *pinstance); ++extern VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance); ++extern VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance); ++extern VCHIQ_STATUS_T vchiq_add_service(VCHIQ_INSTANCE_T instance, int fourcc, VCHIQ_CALLBACK_T callback, void *userdata, VCHIQ_SERVICE_HANDLE_T *pservice); ++extern VCHIQ_STATUS_T vchiq_open_service(VCHIQ_INSTANCE_T instance, int fourcc, VCHIQ_CALLBACK_T callback, void *userdata, VCHIQ_SERVICE_HANDLE_T *pservice); ++extern VCHIQ_STATUS_T vchiq_add_service_params(VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *pservice); ++extern VCHIQ_STATUS_T vchiq_open_service_params(VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *pservice); ++extern VCHIQ_STATUS_T vchiq_close_service(VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_use_service(VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_release_service(VCHIQ_SERVICE_HANDLE_T service); ++ ++extern VCHIQ_STATUS_T vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T service, const VCHIQ_ELEMENT_T *elements, int count); ++extern void vchiq_release_message(VCHIQ_SERVICE_HANDLE_T service, VCHIQ_HEADER_T *header); ++extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, const void *data, int size, void *userdata); ++extern VCHIQ_STATUS_T vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, void *data, int size, void *userdata); ++extern VCHIQ_STATUS_T vchiq_queue_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, const void *offset, int size, void *userdata); ++extern VCHIQ_STATUS_T vchiq_queue_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, void *offset, int size, void *userdata); ++extern VCHIQ_STATUS_T vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T service, const void *data, int size, void *userdata, VCHIQ_BULK_MODE_T mode); ++extern VCHIQ_STATUS_T vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T service, void *data, int size, void *userdata, VCHIQ_BULK_MODE_T mode); ++extern VCHIQ_STATUS_T vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, const void *offset, int size, void *userdata, VCHIQ_BULK_MODE_T mode); ++extern VCHIQ_STATUS_T vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T service, VCHI_MEM_HANDLE_T handle, void *offset, int size, void *userdata, VCHIQ_BULK_MODE_T mode); ++extern int vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T service); ++extern VCHIQ_STATUS_T vchiq_get_config(VCHIQ_INSTANCE_T instance, int config_size, VCHIQ_CONFIG_T *pconfig); ++extern VCHIQ_STATUS_T vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T service, VCHIQ_SERVICE_OPTION_T option, int value); ++ ++extern VCHIQ_STATUS_T vchiq_dump_phys_mem( VCHIQ_SERVICE_HANDLE_T service, void *ptr, size_t num_bytes ); ++ ++#endif /* VCHIQ_IF_H */ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h +@@ -0,0 +1,105 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef VCHIQ_IOCTLS_H ++#define VCHIQ_IOCTLS_H ++ ++#include <linux/ioctl.h> ++#include "vchiq_if.h" ++ ++#define VCHIQ_IOC_MAGIC 0xc4 ++#define VCHIQ_INVALID_HANDLE -1 ++ ++typedef struct { ++ VCHIQ_SERVICE_PARAMS_T params; ++ int is_open; ++ int is_vchi; ++ int handle; /* OUT */ ++} VCHIQ_CREATE_SERVICE_T; ++ ++typedef struct { ++ int handle; ++ int count; ++ const VCHIQ_ELEMENT_T *elements; ++} VCHIQ_QUEUE_MESSAGE_T; ++ ++typedef struct { ++ int handle; ++ void *data; ++ int size; ++ void *userdata; ++ VCHIQ_BULK_MODE_T mode; ++} VCHIQ_QUEUE_BULK_TRANSFER_T; ++ ++typedef struct { ++ VCHIQ_REASON_T reason; ++ VCHIQ_HEADER_T *header; ++ void *service_userdata; ++ void *bulk_userdata; ++} VCHIQ_COMPLETION_DATA_T; ++ ++typedef struct { ++ int count; ++ VCHIQ_COMPLETION_DATA_T *buf; ++ int msgbufsize; ++ int msgbufcount; /* IN/OUT */ ++ void **msgbufs; ++} VCHIQ_AWAIT_COMPLETION_T; ++ ++typedef struct { ++ int handle; ++ int blocking; ++ int bufsize; ++ void *buf; ++} VCHIQ_DEQUEUE_MESSAGE_T; ++ ++typedef struct { ++ int config_size; ++ VCHIQ_CONFIG_T *pconfig; ++} VCHIQ_GET_CONFIG_T; ++ ++typedef struct { ++ int handle; ++ VCHIQ_SERVICE_OPTION_T option; ++ int value; ++} VCHIQ_SET_SERVICE_OPTION_T; ++ ++typedef struct { ++ void *virt_addr; ++ size_t num_bytes; ++} VCHIQ_DUMP_MEM_T; ++ ++#define VCHIQ_IOC_CONNECT _IO(VCHIQ_IOC_MAGIC, 0) ++#define VCHIQ_IOC_SHUTDOWN _IO(VCHIQ_IOC_MAGIC, 1) ++#define VCHIQ_IOC_CREATE_SERVICE _IOWR(VCHIQ_IOC_MAGIC, 2, VCHIQ_CREATE_SERVICE_T) ++#define VCHIQ_IOC_REMOVE_SERVICE _IO(VCHIQ_IOC_MAGIC, 3) ++#define VCHIQ_IOC_QUEUE_MESSAGE _IOW(VCHIQ_IOC_MAGIC, 4, VCHIQ_QUEUE_MESSAGE_T) ++#define VCHIQ_IOC_QUEUE_BULK_TRANSMIT _IOW(VCHIQ_IOC_MAGIC, 5, VCHIQ_QUEUE_BULK_TRANSFER_T) ++#define VCHIQ_IOC_QUEUE_BULK_RECEIVE _IOW(VCHIQ_IOC_MAGIC, 6, VCHIQ_QUEUE_BULK_TRANSFER_T) ++#define VCHIQ_IOC_AWAIT_COMPLETION _IOW(VCHIQ_IOC_MAGIC, 7, VCHIQ_AWAIT_COMPLETION_T) ++#define VCHIQ_IOC_DEQUEUE_MESSAGE _IOW(VCHIQ_IOC_MAGIC, 8, VCHIQ_DEQUEUE_MESSAGE_T) ++#define VCHIQ_IOC_GET_CLIENT_ID _IO(VCHIQ_IOC_MAGIC, 9) ++#define VCHIQ_IOC_GET_CONFIG _IOW(VCHIQ_IOC_MAGIC, 10, VCHIQ_GET_CONFIG_T) ++#define VCHIQ_IOC_CLOSE_SERVICE _IO(VCHIQ_IOC_MAGIC, 11) ++#define VCHIQ_IOC_USE_SERVICE _IO(VCHIQ_IOC_MAGIC, 12) ++#define VCHIQ_IOC_RELEASE_SERVICE _IO(VCHIQ_IOC_MAGIC, 13) ++#define VCHIQ_IOC_SET_SERVICE_OPTION _IOW(VCHIQ_IOC_MAGIC, 14, VCHIQ_SET_SERVICE_OPTION_T) ++#define VCHIQ_IOC_DUMP_PHYS_MEM _IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM_T) ++#define VCHIQ_IOC_MAX 15 ++ ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_kern_lib.c +@@ -0,0 +1,297 @@ ++/***************************************************************************** ++* Copyright 2001 - 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++/* ---- Include Files ---------------------------------------------------- */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++ ++#include "vchiq_core.h" ++#include "vchiq_arm.h" ++#include "interface/vcos/vcos_logging.h" ++ ++/* ---- Public Variables ------------------------------------------------- */ ++ ++extern VCOS_LOG_CAT_T vchiq_core_log_category; ++#define VCOS_LOG_CATEGORY (&vchiq_core_log_category) ++ ++/* ---- Private Constants and Types -------------------------------------- */ ++ ++struct vchiq_instance_struct { ++ VCHIQ_STATE_T *state; ++ ++ int connected; ++}; ++ ++/**************************************************************************** ++* ++* vchiq_initialise ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_initialise( VCHIQ_INSTANCE_T *instanceOut ) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ VCHIQ_STATE_T *state; ++ VCHIQ_INSTANCE_T instance = NULL; ++ ++ vcos_log_trace( "%s called", __func__ ); ++ ++ state = vchiq_get_state(); ++ if (!state) ++ { ++ printk( KERN_ERR "%s: videocore not initialized\n", __func__ ); ++ goto failed; ++ } ++ ++ instance = kzalloc( sizeof(*instance), GFP_KERNEL ); ++ if( !instance ) ++ { ++ printk( KERN_ERR "%s: error allocating vchiq instance\n", __func__ ); ++ goto failed; ++ } ++ ++ instance->connected = 0; ++ instance->state = state; ++ ++ *instanceOut = instance; ++ ++ status = VCHIQ_SUCCESS; ++ ++failed: ++ vcos_log_trace( "%s(%p): returning %d", __func__, instance, status ); ++ ++ return status; ++} ++ ++/**************************************************************************** ++* ++* vchiq_shutdown ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_shutdown( VCHIQ_INSTANCE_T instance ) ++{ ++ VCHIQ_STATUS_T status; ++ VCHIQ_STATE_T *state = instance->state; ++ ++ vcos_log_trace( "%s(%p) called", __func__, instance ); ++ ++ vcos_mutex_lock(&state->mutex); ++ ++ /* Remove all services */ ++ status = vchiq_shutdown_internal(state, instance); ++ ++ vcos_mutex_unlock(&state->mutex); ++ ++ if (status == VCHIQ_SUCCESS) ++ kfree(instance); ++ ++ vcos_log_trace( "%s(%p): returning %d", __func__, instance, status ); ++ ++ return status; ++} ++ ++/**************************************************************************** ++* ++* vchiq_is_connected ++* ++***************************************************************************/ ++ ++int vchiq_is_connected(VCHIQ_INSTANCE_T instance) ++{ ++ return instance->connected; ++} ++ ++/**************************************************************************** ++* ++* vchiq_connect ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_connect(VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_STATUS_T status; ++ VCHIQ_STATE_T *state = instance->state; ++ ++ vcos_log_trace( "%s(%p) called", __func__, instance ); ++ ++ if (vcos_mutex_lock(&state->mutex) != VCOS_SUCCESS) { ++ vcos_log_trace( "%s: call to vcos_mutex_lock failed", __func__ ); ++ status = VCHIQ_RETRY; ++ goto failed; ++ } ++ status = vchiq_connect_internal(state, instance); ++ ++ if (status == VCHIQ_SUCCESS) ++ instance->connected = 1; ++ ++ vcos_mutex_unlock(&state->mutex); ++ ++failed: ++ vcos_log_trace( "%s(%p): returning %d", __func__, instance, status ); ++ ++ return status; ++} ++ ++/**************************************************************************** ++* ++* vchiq_add_service ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_add_service( ++ VCHIQ_INSTANCE_T instance, ++ int fourcc, ++ VCHIQ_CALLBACK_T callback, ++ void *userdata, ++ VCHIQ_SERVICE_HANDLE_T *pservice) ++{ ++ VCHIQ_SERVICE_PARAMS_T params; ++ ++ params.fourcc = fourcc; ++ params.callback = callback; ++ params.userdata = userdata; ++ params.version = 0; ++ params.version_min = 0; ++ ++ return vchiq_add_service_params(instance, ¶ms, pservice); ++} ++ ++/**************************************************************************** ++* ++* vchiq_open_service ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_open_service( ++ VCHIQ_INSTANCE_T instance, ++ int fourcc, ++ VCHIQ_CALLBACK_T callback, ++ void *userdata, ++ VCHIQ_SERVICE_HANDLE_T *pservice) ++{ ++ VCHIQ_SERVICE_PARAMS_T params; ++ ++ params.fourcc = fourcc; ++ params.callback = callback; ++ params.userdata = userdata; ++ params.version = 0; ++ params.version_min = 0; ++ ++ return vchiq_open_service_params(instance, ¶ms, pservice); ++} ++ ++/**************************************************************************** ++* ++* vchiq_add_service_params ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_add_service_params( ++ VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *pservice) ++{ ++ VCHIQ_STATUS_T status; ++ VCHIQ_STATE_T *state = instance->state; ++ VCHIQ_SERVICE_T *service; ++ int srvstate; ++ ++ vcos_log_trace( "%s(%p) called", __func__, instance ); ++ ++ *pservice = NULL; ++ ++ srvstate = vchiq_is_connected( instance ) ++ ? VCHIQ_SRVSTATE_LISTENING ++ : VCHIQ_SRVSTATE_HIDDEN; ++ ++ vcos_mutex_lock(&state->mutex); ++ ++ service = vchiq_add_service_internal( ++ state, ++ params, ++ srvstate, ++ instance); ++ ++ vcos_mutex_unlock(&state->mutex); ++ ++ if ( service ) ++ { ++ *pservice = &service->base; ++ status = VCHIQ_SUCCESS; ++ } ++ else ++ { ++ status = VCHIQ_ERROR; ++ } ++ ++ vcos_log_trace( "%s(%p): returning %d", __func__, instance, status ); ++ ++ return status; ++} ++ ++/**************************************************************************** ++* ++* vchiq_open_service_params ++* ++***************************************************************************/ ++ ++VCHIQ_STATUS_T vchiq_open_service_params( ++ VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *pservice) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_ERROR; ++ VCHIQ_STATE_T *state = instance->state; ++ VCHIQ_SERVICE_T *service; ++ ++ vcos_log_trace( "%s(%p) called", __func__, instance ); ++ ++ *pservice = NULL; ++ ++ if (!vchiq_is_connected(instance)) ++ goto failed; ++ ++ vcos_mutex_lock(&state->mutex); ++ ++ service = vchiq_add_service_internal(state, ++ params, ++ VCHIQ_SRVSTATE_OPENING, ++ instance); ++ ++ vcos_mutex_unlock(&state->mutex); ++ ++ if ( service ) ++ { ++ status = vchiq_open_service_internal(service, current->pid); ++ if ( status == VCHIQ_SUCCESS ) ++ *pservice = &service->base; ++ else ++ vchiq_remove_service(&service->base); ++ } ++ ++failed: ++ vcos_log_trace( "%s(%p): returning %d", __func__, instance, status ); ++ ++ return status; ++} ++ ++EXPORT_SYMBOL(vchiq_initialise); ++EXPORT_SYMBOL(vchiq_shutdown); ++EXPORT_SYMBOL(vchiq_connect); ++EXPORT_SYMBOL(vchiq_add_service); ++EXPORT_SYMBOL(vchiq_open_service); ++EXPORT_SYMBOL(vchiq_add_service_params); ++EXPORT_SYMBOL(vchiq_open_service_params); +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_lib.c +@@ -0,0 +1,1518 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <unistd.h> ++#include <fcntl.h> ++#include <sys/ioctl.h> ++#include <stdio.h> ++ ++#include "vchiq.h" ++#include "vchiq_cfg.h" ++#include "vchiq_ioctl.h" ++#include "interface/vchi/vchi.h" ++#include "interface/vchi/common/endian.h" ++#include "interface/vcos/vcos.h" ++ ++#define VCHIQ_MAX_INSTANCE_SERVICES 32 ++#define MSGBUF_SIZE (VCHIQ_MAX_MSG_SIZE + sizeof(VCHIQ_HEADER_T)) ++ ++#define RETRY(r,x) do { r = x; } while ((r == -1) && (errno == EINTR)) ++ ++#define VCOS_LOG_CATEGORY (&vchiq_lib_log_category) ++ ++typedef struct vchiq_service_struct ++{ ++ VCHIQ_SERVICE_BASE_T base; ++ int handle; ++ int fd; ++ VCHI_CALLBACK_T vchi_callback; ++ void *peek_buf; ++ int peek_size; ++ int client_id; ++} VCHIQ_SERVICE_T; ++ ++typedef struct vchiq_service_struct VCHI_SERVICE_T; ++ ++struct vchiq_instance_struct ++{ ++ int fd; ++ int initialised; ++ int connected; ++ VCOS_THREAD_T completion_thread; ++ VCOS_MUTEX_T mutex; ++ int used_services; ++ VCHIQ_SERVICE_T services[VCHIQ_MAX_INSTANCE_SERVICES]; ++} vchiq_instance; ++ ++typedef struct vchiq_instance_struct VCHI_STATE_T; ++ ++/* Local data */ ++static VCOS_LOG_LEVEL_T vchiq_default_lib_log_level = VCOS_LOG_WARN; ++static VCOS_LOG_CAT_T vchiq_lib_log_category; ++static VCOS_MUTEX_T vchiq_lib_mutex; ++static void *free_msgbufs; ++ ++ ++/* Local utility functions */ ++static VCHIQ_INSTANCE_T ++vchiq_lib_init(void); ++ ++static void *completion_thread(void *); ++ ++static VCHIQ_STATUS_T ++create_service(VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHI_CALLBACK_T vchi_callback, ++ int is_open, ++ VCHIQ_SERVICE_HANDLE_T *pservice); ++ ++static int ++fill_peek_buf(VCHI_SERVICE_T *service, ++ VCHI_FLAGS_T flags); ++ ++static void * ++alloc_msgbuf(void); ++ ++static void ++free_msgbuf(void *buf); ++ ++static __inline int ++is_valid_instance(VCHIQ_INSTANCE_T instance) ++{ ++ return (instance == &vchiq_instance) && (instance->initialised > 0); ++} ++ ++/* ++ * VCHIQ API ++ */ ++ ++VCHIQ_STATUS_T ++vchiq_initialise(VCHIQ_INSTANCE_T *pinstance) ++{ ++ VCHIQ_INSTANCE_T instance; ++ ++ instance = vchiq_lib_init(); ++ ++ vcos_log_trace( "%s: returning instance handle %p", __func__, instance ); ++ ++ *pinstance = instance; ++ ++ return (instance != NULL) ? VCHIQ_SUCCESS : VCHIQ_ERROR; ++} ++ ++VCHIQ_STATUS_T ++vchiq_shutdown(VCHIQ_INSTANCE_T instance) ++{ ++ vcos_log_trace( "%s called", __func__ ); ++ ++ if (!is_valid_instance(instance)) ++ return VCHIQ_ERROR; ++ ++ vcos_mutex_lock(&instance->mutex); ++ ++ if (instance->initialised == 1) ++ { ++ int i; ++ ++ instance->initialised = -1; /* Enter limbo */ ++ ++ /* Remove all services */ ++ ++ for (i = 0; i < instance->used_services; i++) ++ { ++ if (instance->services[i].handle != VCHIQ_INVALID_HANDLE) ++ { ++ vchiq_remove_service(&instance->services[i].base); ++ instance->services[i].handle = VCHIQ_INVALID_HANDLE; ++ } ++ } ++ ++ if (instance->connected) ++ { ++ int ret; ++ RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_SHUTDOWN, 0)); ++ vcos_assert(ret == 0); ++ vcos_thread_join(&instance->completion_thread, NULL); ++ instance->connected = 0; ++ } ++ ++ close(instance->fd); ++ instance->fd = -1; ++ } ++ else if (instance->initialised > 1) ++ { ++ instance->initialised--; ++ } ++ ++ vcos_mutex_unlock(&instance->mutex); ++ ++ vcos_global_lock(); ++ ++ if (instance->initialised == -1) ++ { ++ vcos_mutex_delete(&instance->mutex); ++ instance->initialised = 0; ++ } ++ ++ vcos_global_unlock(); ++ ++ vcos_log_trace( "%s returning", __func__ ); ++ ++ return VCHIQ_SUCCESS; ++} ++ ++VCHIQ_STATUS_T ++vchiq_connect(VCHIQ_INSTANCE_T instance) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ ++ vcos_log_trace( "%s called", __func__ ); ++ ++ if (!is_valid_instance(instance)) ++ return VCHIQ_ERROR; ++ ++ vcos_mutex_lock(&instance->mutex); ++ ++ if (!instance->connected) ++ { ++ int ret = ioctl(instance->fd, VCHIQ_IOC_CONNECT, 0); ++ if (ret == 0) ++ { ++ VCOS_THREAD_ATTR_T attrs; ++ instance->connected = 1; ++ vcos_thread_attr_init(&attrs); ++ vcos_thread_create(&instance->completion_thread, "VCHIQ completion", ++ &attrs, completion_thread, instance); ++ } ++ else ++ { ++ status = VCHIQ_ERROR; ++ } ++ } ++ ++ vcos_mutex_unlock(&instance->mutex); ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_add_service(VCHIQ_INSTANCE_T instance, ++ int fourcc, ++ VCHIQ_CALLBACK_T callback, ++ void *userdata, ++ VCHIQ_SERVICE_HANDLE_T *pservice) ++{ ++ VCHIQ_SERVICE_PARAMS_T params; ++ ++ params.fourcc = fourcc; ++ params.callback = callback; ++ params.userdata = userdata; ++ params.version = 0; ++ params.version_min = 0; ++ ++ return vchiq_add_service_params(instance, ¶ms, pservice); ++} ++ ++VCHIQ_STATUS_T ++vchiq_open_service(VCHIQ_INSTANCE_T instance, ++ int fourcc, ++ VCHIQ_CALLBACK_T callback, ++ void *userdata, ++ VCHIQ_SERVICE_HANDLE_T *pservice) ++{ ++ VCHIQ_SERVICE_PARAMS_T params; ++ ++ params.fourcc = fourcc; ++ params.callback = callback; ++ params.userdata = userdata; ++ params.version = 0; ++ params.version_min = 0; ++ ++ return vchiq_open_service_params(instance, ¶ms, pservice); ++} ++ ++VCHIQ_STATUS_T ++vchiq_add_service_params(VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *pservice) ++{ ++ VCHIQ_STATUS_T status; ++ ++ vcos_log_trace( "%s called fourcc = 0x%08x (%c%c%c%c)", ++ __func__, ++ params->fourcc, ++ (params->fourcc >> 24) & 0xff, ++ (params->fourcc >> 16) & 0xff, ++ (params->fourcc >> 8) & 0xff, ++ (params->fourcc ) & 0xff ); ++ ++ if (!params->callback) ++ return VCHIQ_ERROR; ++ ++ if (!is_valid_instance(instance)) ++ return VCHIQ_ERROR; ++ ++ status = create_service(instance, ++ params, ++ NULL/*vchi_callback*/, ++ 0/*!open*/, ++ pservice); ++ ++ vcos_log_trace( "%s returning service handle = 0x%08x", __func__, (uint32_t)*pservice ); ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_open_service_params(VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHIQ_SERVICE_HANDLE_T *pservice) ++{ ++ VCHIQ_STATUS_T status; ++ ++ vcos_log_trace( "%s called fourcc = 0x%08x (%c%c%c%c)", ++ __func__, ++ params->fourcc, ++ (params->fourcc >> 24) & 0xff, ++ (params->fourcc >> 16) & 0xff, ++ (params->fourcc >> 8) & 0xff, ++ (params->fourcc ) & 0xff ); ++ ++ if (!params->callback) ++ return VCHIQ_ERROR; ++ ++ if (!is_valid_instance(instance)) ++ return VCHIQ_ERROR; ++ ++ status = create_service(instance, ++ params, ++ NULL/*vchi_callback*/, ++ 1/*open*/, ++ pservice); ++ ++ vcos_log_trace( "%s returning service handle = 0x%08x", __func__, (uint32_t)*pservice ); ++ ++ return status; ++} ++ ++VCHIQ_STATUS_T ++vchiq_close_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ int ret; ++ ++ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); ++ ++ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_CLOSE_SERVICE, service->handle)); ++ ++ if (ret != 0) ++ return VCHIQ_ERROR; ++ ++ service->handle = VCHIQ_INVALID_HANDLE; ++ return VCHIQ_SUCCESS; ++} ++ ++VCHIQ_STATUS_T ++vchiq_remove_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ int ret; ++ ++ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); ++ ++ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle)); ++ ++ if (ret != 0) ++ return VCHIQ_ERROR; ++ ++ service->handle = VCHIQ_INVALID_HANDLE; ++ return VCHIQ_SUCCESS; ++} ++ ++VCHIQ_STATUS_T ++vchiq_queue_message(VCHIQ_SERVICE_HANDLE_T handle, ++ const VCHIQ_ELEMENT_T *elements, ++ int count) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ VCHIQ_QUEUE_MESSAGE_T args; ++ int ret; ++ ++ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); ++ ++ args.handle = service->handle; ++ args.elements = elements; ++ args.count = count; ++ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args)); ++ ++ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; ++} ++ ++void ++vchiq_release_message(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHIQ_HEADER_T *header) ++{ ++ vcos_log_trace( "%s handle=%08x, header=%x", __func__, (uint32_t)handle, (uint32_t)header ); ++ ++ free_msgbuf(header); ++} ++ ++VCHIQ_STATUS_T ++vchiq_queue_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, ++ const void *data, ++ int size, ++ void *userdata) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ VCHIQ_QUEUE_BULK_TRANSFER_T args; ++ int ret; ++ ++ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); ++ ++ args.handle = service->handle; ++ args.data = (void *)data; ++ args.size = size; ++ args.userdata = userdata; ++ args.mode = VCHIQ_BULK_MODE_CALLBACK; ++ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args)); ++ ++ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; ++} ++ ++VCHIQ_STATUS_T ++vchiq_queue_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, ++ void *data, ++ int size, ++ void *userdata) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ VCHIQ_QUEUE_BULK_TRANSFER_T args; ++ int ret; ++ ++ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); ++ ++ args.handle = service->handle; ++ args.data = data; ++ args.size = size; ++ args.userdata = userdata; ++ args.mode = VCHIQ_BULK_MODE_CALLBACK; ++ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args)); ++ ++ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; ++} ++ ++VCHIQ_STATUS_T ++vchiq_queue_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T memhandle, ++ const void *offset, ++ int size, ++ void *userdata) ++{ ++ vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID); ++ ++ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); ++ ++ return vchiq_queue_bulk_transmit(handle, offset, size, userdata); ++} ++ ++VCHIQ_STATUS_T ++vchiq_queue_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T memhandle, ++ void *offset, ++ int size, ++ void *userdata) ++{ ++ vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID); ++ ++ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); ++ ++ return vchiq_queue_bulk_receive(handle, offset, size, userdata); ++} ++ ++VCHIQ_STATUS_T ++vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE_T handle, ++ const void *data, ++ int size, ++ void *userdata, ++ VCHIQ_BULK_MODE_T mode) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ VCHIQ_QUEUE_BULK_TRANSFER_T args; ++ int ret; ++ ++ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); ++ ++ args.handle = service->handle; ++ args.data = (void *)data; ++ args.size = size; ++ args.userdata = userdata; ++ args.mode = mode; ++ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args)); ++ ++ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; ++} ++ ++VCHIQ_STATUS_T ++vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_T handle, ++ void *data, ++ int size, ++ void *userdata, ++ VCHIQ_BULK_MODE_T mode) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ VCHIQ_QUEUE_BULK_TRANSFER_T args; ++ int ret; ++ ++ vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); ++ ++ args.handle = service->handle; ++ args.data = data; ++ args.size = size; ++ args.userdata = userdata; ++ args.mode = mode; ++ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args)); ++ ++ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; ++} ++ ++VCHIQ_STATUS_T ++vchiq_bulk_transmit_handle(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T memhandle, ++ const void *offset, ++ int size, ++ void *userdata, ++ VCHIQ_BULK_MODE_T mode) ++{ ++ vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID); ++ ++ return vchiq_bulk_transmit(handle, offset, size, userdata, mode); ++} ++ ++VCHIQ_STATUS_T ++vchiq_bulk_receive_handle(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T memhandle, ++ void *offset, ++ int size, ++ void *userdata, ++ VCHIQ_BULK_MODE_T mode) ++{ ++ vcos_assert(memhandle == VCHI_MEM_HANDLE_INVALID); ++ ++ return vchiq_bulk_receive(handle, offset, size, userdata, mode); ++} ++ ++int ++vchiq_get_client_id(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ ++ return ioctl(service->fd, VCHIQ_IOC_GET_CLIENT_ID, service->handle); ++} ++ ++VCHIQ_STATUS_T ++vchiq_get_config(VCHIQ_INSTANCE_T instance, ++ int config_size, ++ VCHIQ_CONFIG_T *pconfig) ++{ ++ VCHIQ_GET_CONFIG_T args; ++ int ret; ++ ++ if (!is_valid_instance(instance)) ++ return VCHIQ_ERROR; ++ ++ args.config_size = config_size; ++ args.pconfig = pconfig; ++ ++ RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_GET_CONFIG, &args)); ++ ++ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; ++} ++ ++int32_t ++vchiq_use_service( const VCHIQ_SERVICE_HANDLE_T handle ) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ int ret; ++ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_USE_SERVICE, service->handle)); ++ return ret; ++} ++ ++int32_t ++vchiq_release_service( const VCHIQ_SERVICE_HANDLE_T handle ) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ int ret; ++ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_RELEASE_SERVICE, service->handle)); ++ return ret; ++} ++ ++VCHIQ_STATUS_T ++vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T handle, ++ VCHIQ_SERVICE_OPTION_T option, int value) ++{ ++ VCHIQ_SET_SERVICE_OPTION_T args; ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ int ret; ++ ++ args.handle = service->handle; ++ args.option = option; ++ args.value = value; ++ ++ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_SET_SERVICE_OPTION, &args)); ++ ++ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; ++} ++ ++/* ++ * VCHI API ++ */ ++ ++/* ---------------------------------------------------------------------- ++ * return pointer to the mphi message driver function table ++ * -------------------------------------------------------------------- */ ++const VCHI_MESSAGE_DRIVER_T * ++vchi_mphi_message_driver_func_table( void ) ++{ ++ return NULL; ++} ++ ++/* ---------------------------------------------------------------------- ++ * return a pointer to the 'single' connection driver fops ++ * -------------------------------------------------------------------- */ ++const VCHI_CONNECTION_API_T * ++single_get_func_table( void ) ++{ ++ return NULL; ++} ++ ++VCHI_CONNECTION_T * ++vchi_create_connection( const VCHI_CONNECTION_API_T * function_table, ++ const VCHI_MESSAGE_DRIVER_T * low_level ) ++{ ++ vcos_unused(function_table); ++ vcos_unused(low_level); ++ ++ return NULL; ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_peek ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle, ++ * void **data, ++ * uint32_t *msg_size, ++ * VCHI_FLAGS_T flags ++ * ++ * Description: Routine to return a pointer to the current message (to allow in place processing) ++ * The message can be removed using vchi_msg_remove when you're finished ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t ++vchi_msg_peek( VCHI_SERVICE_HANDLE_T handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags ) ++{ ++ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; ++ int ret; ++ ++ ret = fill_peek_buf(service, flags); ++ ++ if (ret == 0) ++ { ++ *data = service->peek_buf; ++ *msg_size = service->peek_size; ++ } ++ ++ return ret; ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_remove ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle, ++ * ++ * Description: Routine to remove a message (after it has been read with vchi_msg_peek) ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t ++vchi_msg_remove( VCHI_SERVICE_HANDLE_T handle ) ++{ ++ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; ++ ++ /* Why would you call vchi_msg_remove without calling vchi_msg_peek first? */ ++ vcos_assert(service->peek_size >= 0); ++ ++ /* Invalidate the content but reuse the buffer */ ++ service->peek_size = -1; ++ ++ return 0; ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_queue ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * const void *data, ++ * uint32_t data_size, ++ * VCHI_FLAGS_T flags, ++ * void *msg_handle, ++ * ++ * Description: Thin wrapper to queue a message onto a connection ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t ++vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle, ++ const void * data, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void * msg_handle ) ++{ ++ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; ++ VCHIQ_QUEUE_MESSAGE_T args; ++ VCHIQ_ELEMENT_T element = {data, data_size}; ++ int ret; ++ ++ vcos_unused(msg_handle); ++ vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED); ++ ++ args.handle = service->handle; ++ args.elements = &element; ++ args.count = 1; ++ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args)); ++ ++ return ret; ++} ++ ++/*********************************************************** ++ * Name: vchi_bulk_queue_receive ++ * ++ * Arguments: VCHI_BULK_HANDLE_T handle, ++ * void *data_dst, ++ * const uint32_t data_size, ++ * VCHI_FLAGS_T flags ++ * void *bulk_handle ++ * ++ * Description: Routine to setup a rcv buffer ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t ++vchi_bulk_queue_receive( VCHI_SERVICE_HANDLE_T handle, ++ void * data_dst, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void * bulk_handle ) ++{ ++ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; ++ VCHIQ_QUEUE_BULK_TRANSFER_T args; ++ int ret; ++ ++ switch ((int)flags) { ++ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ args.mode = VCHIQ_BULK_MODE_CALLBACK; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: ++ args.mode = VCHIQ_BULK_MODE_BLOCKING; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ case VCHI_FLAGS_NONE: ++ args.mode = VCHIQ_BULK_MODE_NOCALLBACK; ++ break; ++ default: ++ vcos_assert(0); ++ break; ++ } ++ ++ args.handle = service->handle; ++ args.data = data_dst; ++ args.size = data_size; ++ args.userdata = bulk_handle; ++ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_RECEIVE, &args)); ++ ++ return ret; ++} ++ ++/*********************************************************** ++ * Name: vchi_bulk_queue_transmit ++ * ++ * Arguments: VCHI_BULK_HANDLE_T handle, ++ * const void *data_src, ++ * uint32_t data_size, ++ * VCHI_FLAGS_T flags, ++ * void *bulk_handle ++ * ++ * Description: Routine to transmit some data ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t ++vchi_bulk_queue_transmit( VCHI_SERVICE_HANDLE_T handle, ++ const void * data_src, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void * bulk_handle ) ++{ ++ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; ++ VCHIQ_QUEUE_BULK_TRANSFER_T args; ++ int ret; ++ ++ switch ((int)flags) { ++ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ args.mode = VCHIQ_BULK_MODE_CALLBACK; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ: ++ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: ++ args.mode = VCHIQ_BULK_MODE_BLOCKING; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ case VCHI_FLAGS_NONE: ++ args.mode = VCHIQ_BULK_MODE_NOCALLBACK; ++ break; ++ default: ++ vcos_assert(0); ++ break; ++ } ++ ++ args.handle = service->handle; ++ args.data = (void *)data_src; ++ args.size = data_size; ++ args.userdata = bulk_handle; ++ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_BULK_TRANSMIT, &args)); ++ ++ return ret; ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_dequeue ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * void *data, ++ * uint32_t max_data_size_to_read, ++ * uint32_t *actual_msg_size ++ * VCHI_FLAGS_T flags ++ * ++ * Description: Routine to dequeue a message into the supplied buffer ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t ++vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T handle, ++ void *data, ++ uint32_t max_data_size_to_read, ++ uint32_t *actual_msg_size, ++ VCHI_FLAGS_T flags ) ++{ ++ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; ++ VCHIQ_DEQUEUE_MESSAGE_T args; ++ int ret; ++ ++ vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); ++ ++ if (service->peek_size >= 0) ++ { ++ fprintf(stderr, "vchi_msg_dequeue -> using peek buffer\n"); ++ if ((uint32_t)service->peek_size <= max_data_size_to_read) ++ { ++ memcpy(data, service->peek_buf, service->peek_size); ++ *actual_msg_size = service->peek_size; ++ /* Invalidate the peek data, but retain the buffer */ ++ service->peek_size = -1; ++ ret = 0; ++ } ++ else ++ { ++ ret = -1; ++ } ++ } ++ else ++ { ++ args.handle = service->handle; ++ args.blocking = (flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); ++ args.bufsize = max_data_size_to_read; ++ args.buf = data; ++ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_DEQUEUE_MESSAGE, &args)); ++ if (ret >= 0) ++ { ++ *actual_msg_size = ret; ++ ret = 0; ++ } ++ } ++ ++ if ((ret < 0) && (errno != EWOULDBLOCK)) ++ fprintf(stderr, "vchi_msg_dequeue -> %d(%d)\n", ret, errno); ++ ++ return ret; ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_queuev ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * const void *data, ++ * uint32_t data_size, ++ * VCHI_FLAGS_T flags, ++ * void *msg_handle ++ * ++ * Description: Thin wrapper to queue a message onto a connection ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++ ++vcos_static_assert(sizeof(VCHI_MSG_VECTOR_T) == sizeof(VCHIQ_ELEMENT_T)); ++vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_base) == offsetof(VCHIQ_ELEMENT_T, data)); ++vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_len) == offsetof(VCHIQ_ELEMENT_T, size)); ++ ++int32_t ++vchi_msg_queuev( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_VECTOR_T * vector, ++ uint32_t count, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ) ++{ ++ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; ++ VCHIQ_QUEUE_MESSAGE_T args; ++ int ret; ++ ++ vcos_unused(msg_handle); ++ ++ vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED); ++ ++ args.handle = service->handle; ++ args.elements = (const VCHIQ_ELEMENT_T *)vector; ++ args.count = count; ++ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_QUEUE_MESSAGE, &args)); ++ ++ return ret; ++} ++ ++/*********************************************************** ++ * Name: vchi_held_msg_release ++ * ++ * Arguments: VCHI_HELD_MSG_T *message ++ * ++ * Description: Routine to release a held message (after it has been read with vchi_msg_hold) ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t ++vchi_held_msg_release( VCHI_HELD_MSG_T *message ) ++{ ++ int ret = -1; ++ ++ if (message && message->message && !message->service) ++ { ++ free_msgbuf(message->message); ++ ret = 0; ++ } ++ ++ return ret; ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_hold ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * void **data, ++ * uint32_t *msg_size, ++ * VCHI_FLAGS_T flags, ++ * VCHI_HELD_MSG_T *message_handle ++ * ++ * Description: Routine to return a pointer to the current message (to allow in place processing) ++ * The message is dequeued - don't forget to release the message using ++ * vchi_held_msg_release when you're finished ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t ++vchi_msg_hold( VCHI_SERVICE_HANDLE_T handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags, ++ VCHI_HELD_MSG_T *message_handle ) ++{ ++ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; ++ int ret; ++ ++ ret = fill_peek_buf(service, flags); ++ ++ if (ret == 0) ++ { ++ *data = service->peek_buf; ++ *msg_size = service->peek_size; ++ ++ message_handle->message = service->peek_buf; ++ message_handle->service = NULL; ++ ++ service->peek_size = -1; ++ service->peek_buf = NULL; ++ } ++ ++ return 0; ++} ++ ++/*********************************************************** ++ * Name: vchi_initialise ++ * ++ * Arguments: VCHI_INSTANCE_T *instance_handle ++ * VCHI_CONNECTION_T **connections ++ * const uint32_t num_connections ++ * ++ * Description: Initialises the hardware but does not transmit anything ++ * When run as a Host App this will be called twice hence the need ++ * to malloc the state information ++ * ++ * Returns: 0 if successful, failure otherwise ++ * ++ ***********************************************************/ ++int32_t ++vchi_initialise( VCHI_INSTANCE_T *instance_handle ) ++{ ++ VCHIQ_INSTANCE_T instance; ++ ++ instance = vchiq_lib_init(); ++ ++ vcos_log_trace( "%s: returning instance handle %p", __func__, instance ); ++ ++ *instance_handle = (VCHI_INSTANCE_T)instance; ++ ++ return (instance != NULL) ? 0 : -1; ++} ++ ++/*********************************************************** ++ * Name: vchi_connect ++ * ++ * Arguments: VCHI_CONNECTION_T **connections ++ * const uint32_t num_connections ++ * VCHI_INSTANCE_T instance_handle ) ++ * ++ * Description: Starts the command service on each connection, ++ * causing INIT messages to be pinged back and forth ++ * ++ * Returns: 0 if successful, failure otherwise ++ * ++ ***********************************************************/ ++int32_t ++vchi_connect( VCHI_CONNECTION_T **connections, ++ const uint32_t num_connections, ++ VCHI_INSTANCE_T instance_handle ) ++{ ++ VCHIQ_STATUS_T status; ++ ++ vcos_unused(connections); ++ vcos_unused(num_connections); ++ ++ status = vchiq_connect((VCHIQ_INSTANCE_T)instance_handle); ++ ++ return (status == VCHIQ_SUCCESS) ? 0 : -1; ++} ++ ++ ++/*********************************************************** ++ * Name: vchi_disconnect ++ * ++ * Arguments: VCHI_INSTANCE_T instance_handle ++ * ++ * Description: Stops the command service on each connection, ++ * causing DE-INIT messages to be pinged back and forth ++ * ++ * Returns: 0 if successful, failure otherwise ++ * ++ ***********************************************************/ ++int32_t ++vchi_disconnect( VCHI_INSTANCE_T instance_handle ) ++{ ++ VCHIQ_STATUS_T status; ++ ++ status = vchiq_shutdown((VCHIQ_INSTANCE_T)instance_handle); ++ ++ return (status == VCHIQ_SUCCESS) ? 0 : -1; ++} ++ ++ ++/*********************************************************** ++ * Name: vchi_service_open ++ * Name: vchi_service_create ++ * ++ * Arguments: VCHI_INSTANCE_T *instance_handle ++ * SERVICE_CREATION_T *setup, ++ * VCHI_SERVICE_HANDLE_T *handle ++ * ++ * Description: Routine to open a service ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t ++vchi_service_open( VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, ++ VCHI_SERVICE_HANDLE_T *handle ) ++{ ++ VCHIQ_SERVICE_PARAMS_T params; ++ VCHIQ_STATUS_T status; ++ ++ memset(¶ms, 0, sizeof(params)); ++ params.fourcc = setup->service_id; ++ params.userdata = setup->callback_param; ++ ++ status = create_service((VCHIQ_INSTANCE_T)instance_handle, ++ ¶ms, ++ setup->callback, ++ 1/*open*/, ++ (VCHIQ_SERVICE_HANDLE_T *)handle); ++ ++ return (status == VCHIQ_SUCCESS) ? 0 : -1; ++} ++ ++int32_t ++vchi_service_create( VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, VCHI_SERVICE_HANDLE_T *handle ) ++{ ++ VCHIQ_SERVICE_PARAMS_T params; ++ VCHIQ_STATUS_T status; ++ ++ memset(¶ms, 0, sizeof(params)); ++ params.fourcc = setup->service_id; ++ params.userdata = setup->callback_param; ++ ++ status = create_service((VCHIQ_INSTANCE_T)instance_handle, ++ ¶ms, ++ setup->callback, ++ 0/*!open*/, ++ (VCHIQ_SERVICE_HANDLE_T *)handle); ++ ++ return (status == VCHIQ_SUCCESS) ? 0 : -1; ++} ++ ++int32_t ++vchi_service_close( const VCHI_SERVICE_HANDLE_T handle ) ++{ ++ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; ++ int ret; ++ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle)); ++ ++ if (ret == 0) ++ service->handle = VCHIQ_INVALID_HANDLE; ++ ++ return ret; ++} ++ ++int32_t ++vchi_service_destroy( const VCHI_SERVICE_HANDLE_T handle ) ++{ ++ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; ++ int ret; ++ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle)); ++ ++ if (ret == 0) ++ service->handle = VCHIQ_INVALID_HANDLE; ++ ++ return ret; ++} ++ ++/* ---------------------------------------------------------------------- ++ * read a uint32_t from buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++uint32_t ++vchi_readbuf_uint32( const void *_ptr ) ++{ ++ const unsigned char *ptr = _ptr; ++ return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); ++} ++ ++/* ---------------------------------------------------------------------- ++ * write a uint32_t to buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++void ++vchi_writebuf_uint32( void *_ptr, uint32_t value ) ++{ ++ unsigned char *ptr = _ptr; ++ ptr[0] = (unsigned char)((value >> 0) & 0xFF); ++ ptr[1] = (unsigned char)((value >> 8) & 0xFF); ++ ptr[2] = (unsigned char)((value >> 16) & 0xFF); ++ ptr[3] = (unsigned char)((value >> 24) & 0xFF); ++} ++ ++/* ---------------------------------------------------------------------- ++ * read a uint16_t from buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++uint16_t ++vchi_readbuf_uint16( const void *_ptr ) ++{ ++ const unsigned char *ptr = _ptr; ++ return ptr[0] | (ptr[1] << 8); ++} ++ ++/* ---------------------------------------------------------------------- ++ * write a uint16_t into the buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++void ++vchi_writebuf_uint16( void *_ptr, uint16_t value ) ++{ ++ unsigned char *ptr = _ptr; ++ ptr[0] = (value >> 0) & 0xFF; ++ ptr[1] = (value >> 8) & 0xFF; ++} ++ ++/*********************************************************** ++ * Name: vchi_service_use ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle ++ * ++ * Description: Routine to increment refcount on a service ++ * ++ * Returns: void ++ * ++ ***********************************************************/ ++int32_t ++vchi_service_use( const VCHI_SERVICE_HANDLE_T handle ) ++{ ++ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; ++ int ret; ++ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_USE_SERVICE, service->handle)); ++ return ret; ++} ++ ++/*********************************************************** ++ * Name: vchi_service_release ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle ++ * ++ * Description: Routine to decrement refcount on a service ++ * ++ * Returns: void ++ * ++ ***********************************************************/ ++int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle ) ++{ ++ VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; ++ int ret; ++ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_RELEASE_SERVICE, service->handle)); ++ return ret; ++} ++ ++/* ++ * Support functions ++ */ ++ ++static VCHIQ_INSTANCE_T ++vchiq_lib_init(void) ++{ ++ static int mutex_initialised = 0; ++ static VCOS_MUTEX_T vchiq_lib_mutex; ++ VCHIQ_INSTANCE_T instance = &vchiq_instance; ++ ++ vcos_global_lock(); ++ if (!mutex_initialised) ++ { ++ vcos_mutex_create(&vchiq_lib_mutex, "vchiq-init"); ++ ++ vcos_log_set_level( &vchiq_lib_log_category, vchiq_default_lib_log_level ); ++ vcos_log_register( "vchiq_lib", &vchiq_lib_log_category ); ++ ++ mutex_initialised = 1; ++ } ++ vcos_global_unlock(); ++ ++ vcos_mutex_lock(&vchiq_lib_mutex); ++ ++ if (instance->initialised == 0) ++ { ++ instance->fd = open("/dev/vchiq", O_RDWR); ++ if (instance->fd >= 0) ++ { ++ VCHIQ_GET_CONFIG_T args; ++ VCHIQ_CONFIG_T config; ++ int ret; ++ args.config_size = sizeof(config); ++ args.pconfig = &config; ++ RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_GET_CONFIG, &args)); ++ if ((ret == 0) && (config.version >= VCHIQ_VERSION_MIN) && (config.version_min <= VCHIQ_VERSION)) ++ { ++ instance->used_services = 0; ++ vcos_mutex_create(&instance->mutex, "VCHIQ instance"); ++ instance->initialised = 1; ++ } ++ else ++ { ++ if (ret == 0) ++ { ++ vcos_log_error("Incompatible VCHIQ library - driver version %d (min %d), library version %d (min %d)", ++ config.version, config.version_min, VCHIQ_VERSION, VCHIQ_VERSION_MIN); ++ } ++ else ++ { ++ vcos_log_error("Very incompatible VCHIQ library - cannot retrieve driver version"); ++ } ++ close(instance->fd); ++ instance = NULL; ++ } ++ } ++ else ++ { ++ instance = NULL; ++ } ++ } ++ else if (instance->initialised > 0) ++ { ++ instance->initialised++; ++ } ++ ++ vcos_mutex_unlock(&vchiq_lib_mutex); ++ ++ return instance; ++} ++ ++static void * ++completion_thread(void *arg) ++{ ++ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)arg; ++ VCHIQ_AWAIT_COMPLETION_T args; ++ VCHIQ_COMPLETION_DATA_T completions[8]; ++ void *msgbufs[8]; ++ ++ static const VCHI_CALLBACK_REASON_T vchiq_reason_to_vchi[] = ++ { ++ VCHI_CALLBACK_SERVICE_OPENED, // VCHIQ_SERVICE_OPENED ++ VCHI_CALLBACK_SERVICE_CLOSED, // VCHIQ_SERVICE_CLOSED ++ VCHI_CALLBACK_MSG_AVAILABLE, // VCHIQ_MESSAGE_AVAILABLE ++ VCHI_CALLBACK_BULK_SENT, // VCHIQ_BULK_TRANSMIT_DONE ++ VCHI_CALLBACK_BULK_RECEIVED, // VCHIQ_BULK_RECEIVE_DONE ++ VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, // VCHIQ_BULK_TRANSMIT_ABORTED ++ VCHI_CALLBACK_BULK_RECEIVE_ABORTED, // VCHIQ_BULK_RECEIVE_ABORTED ++ }; ++ ++ args.count = vcos_countof(completions); ++ args.buf = completions; ++ args.msgbufsize = MSGBUF_SIZE; ++ args.msgbufcount = 0; ++ args.msgbufs = msgbufs; ++ ++ while (1) ++ { ++ int ret, i; ++ ++ while ((unsigned int)args.msgbufcount < vcos_countof(msgbufs)) ++ { ++ void *msgbuf = alloc_msgbuf(); ++ if (msgbuf) ++ { ++ msgbufs[args.msgbufcount++] = msgbuf; ++ } ++ else ++ { ++ fprintf(stderr, "vchiq_lib: failed to allocate a message buffer\n"); ++ vcos_demand(args.msgbufcount != 0); ++ } ++ } ++ ++ RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_AWAIT_COMPLETION, &args)); ++ ++ if (ret <= 0) ++ break; ++ ++ for (i = 0; i < ret; i++) ++ { ++ VCHIQ_COMPLETION_DATA_T *completion = &completions[i]; ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)completion->service_userdata; ++ if (service->base.callback) ++ { ++ vcos_log_trace( "callback(%x, %x, %x, %x)", ++ completion->reason, (uint32_t)completion->header, ++ (uint32_t)&service->base, (uint32_t)completion->bulk_userdata ); ++ service->base.callback(completion->reason, completion->header, ++ &service->base, completion->bulk_userdata); ++ } ++ else if (service->vchi_callback) ++ { ++ VCHI_CALLBACK_REASON_T vchi_reason = ++ vchiq_reason_to_vchi[completion->reason]; ++ service->vchi_callback(service->base.userdata, vchi_reason, completion->bulk_userdata); ++ } ++ } ++ } ++ return NULL; ++} ++ ++static VCHIQ_STATUS_T ++create_service(VCHIQ_INSTANCE_T instance, ++ const VCHIQ_SERVICE_PARAMS_T *params, ++ VCHI_CALLBACK_T vchi_callback, ++ int is_open, ++ VCHIQ_SERVICE_HANDLE_T *pservice) ++{ ++ VCHIQ_SERVICE_T *service = NULL; ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ int i; ++ ++ if (!is_valid_instance(instance)) ++ return VCHIQ_ERROR; ++ ++ vcos_mutex_lock(&instance->mutex); ++ ++ /* Find a free service */ ++ if (is_open) ++ { ++ /* Find a free service */ ++ for (i = 0; i < instance->used_services; i++) ++ { ++ if (instance->services[i].handle == VCHIQ_INVALID_HANDLE) ++ { ++ service = &instance->services[i]; ++ break; ++ } ++ } ++ } ++ else ++ { ++ for (i = (instance->used_services - 1); i >= 0; i--) ++ { ++ VCHIQ_SERVICE_T *srv = &instance->services[i]; ++ if (srv->handle == VCHIQ_INVALID_HANDLE) ++ { ++ service = srv; ++ } ++ else if ( ++ (srv->base.fourcc == params->fourcc) && ++ ((srv->base.callback != params->callback) || ++ (srv->vchi_callback != vchi_callback))) ++ { ++ /* There is another server using this fourcc which doesn't match */ ++ service = NULL; ++ status = VCHIQ_ERROR; ++ break; ++ } ++ } ++ } ++ ++ if (!service && (status == VCHIQ_SUCCESS) && ++ (instance->used_services < VCHIQ_MAX_INSTANCE_SERVICES)) ++ service = &instance->services[instance->used_services++]; ++ ++ if (service) ++ { ++ VCHIQ_CREATE_SERVICE_T args; ++ int ret; ++ service->base.fourcc = params->fourcc; ++ service->base.callback = params->callback; ++ service->vchi_callback = vchi_callback; ++ service->base.userdata = params->userdata; ++ service->fd = instance->fd; ++ service->peek_size = -1; ++ service->peek_buf = NULL; ++ ++ args.params = *params; ++ args.params.userdata = service; ++ args.is_open = is_open; ++ args.is_vchi = (params->callback == NULL); ++ args.handle = -1; /* OUT parameter */ ++ RETRY(ret, ioctl(instance->fd, VCHIQ_IOC_CREATE_SERVICE, &args)); ++ if (ret == 0) ++ service->handle = args.handle; ++ else ++ status = VCHIQ_ERROR; ++ } ++ ++ *pservice = (status == VCHIQ_SUCCESS) ? &service->base : NULL; ++ ++ vcos_mutex_unlock(&instance->mutex); ++ ++ return status; ++} ++ ++static int ++fill_peek_buf(VCHI_SERVICE_T *service, ++ VCHI_FLAGS_T flags) ++{ ++ VCHIQ_DEQUEUE_MESSAGE_T args; ++ int ret = 0; ++ ++ vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); ++ ++ if (service->peek_size < 0) ++ { ++ if (!service->peek_buf) ++ service->peek_buf = alloc_msgbuf(); ++ ++ if (service->peek_buf) ++ { ++ args.handle = service->handle; ++ args.blocking = (flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); ++ args.bufsize = MSGBUF_SIZE; ++ args.buf = service->peek_buf; ++ ++ RETRY(ret, ioctl(service->fd, VCHIQ_IOC_DEQUEUE_MESSAGE, &args)); ++ ++ if (ret >= 0) ++ { ++ service->peek_size = ret; ++ ret = 0; ++ } ++ else ++ { ++ ret = -1; ++ } ++ } ++ else ++ { ++ ret = -1; ++ } ++ } ++ ++ return ret; ++} ++ ++ ++static void * ++alloc_msgbuf(void) ++{ ++ void *msgbuf; ++ vcos_mutex_lock(&vchiq_lib_mutex); ++ msgbuf = free_msgbufs; ++ if (msgbuf) ++ free_msgbufs = *(void **)msgbuf; ++ vcos_mutex_unlock(&vchiq_lib_mutex); ++ if (!msgbuf) ++ msgbuf = malloc(MSGBUF_SIZE); ++ return msgbuf; ++} ++ ++static void ++free_msgbuf(void *buf) ++{ ++ vcos_mutex_lock(&vchiq_lib_mutex); ++ *(void **)buf = free_msgbufs; ++ free_msgbufs = buf; ++ vcos_mutex_unlock(&vchiq_lib_mutex); ++} +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_memdrv.h +@@ -0,0 +1,45 @@ ++/***************************************************************************** ++* Copyright 2001 - 2010 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#ifndef VCHIQ_MEMDRV_H ++#define VCHIQ_MEMDRV_H ++ ++/* ---- Include Files ----------------------------------------------------- */ ++ ++#include <linux/kernel.h> ++#include "vchiq_if.h" ++ ++/* ---- Constants and Types ---------------------------------------------- */ ++ ++typedef struct ++{ ++ void *armSharedMemVirt; ++ dma_addr_t armSharedMemPhys; ++ size_t armSharedMemSize; ++ ++ void *vcSharedMemVirt; ++ dma_addr_t vcSharedMemPhys; ++ size_t vcSharedMemSize; ++ ++} VCHIQ_SHARED_MEM_INFO_T; ++ ++/* ---- Variable Externs ------------------------------------------------- */ ++ ++/* ---- Function Prototypes ---------------------------------------------- */ ++ ++void vchiq_get_shared_mem_info( VCHIQ_SHARED_MEM_INFO_T *info ); ++ ++VCHIQ_STATUS_T vchiq_memdrv_initialise(void); ++ ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_pagelist.h +@@ -0,0 +1,43 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef VCHIQ_PAGELIST_H ++#define VCHIQ_PAGELIST_H ++ ++#ifndef PAGE_SIZE ++#define PAGE_SIZE 4096 ++#endif ++#define CACHE_LINE_SIZE 32 ++#define PAGELIST_WRITE 0 ++#define PAGELIST_READ 1 ++#define PAGELIST_READ_WITH_FRAGMENTS 2 ++ ++typedef struct pagelist_struct { ++ unsigned long length; ++ unsigned short type; ++ unsigned short offset; ++ unsigned long addrs[1]; /* N.B. 12 LSBs hold the number of following ++ pages at consecutive addresses. */ ++} PAGELIST_T; ++ ++typedef struct fragments_struct { ++ char headbuf[CACHE_LINE_SIZE]; ++ char tailbuf[CACHE_LINE_SIZE]; ++} FRAGMENTS_T; ++ ++#endif /* VCHIQ_PAGELIST_H */ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c +@@ -0,0 +1,970 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "interface/vchi/vchi.h" ++#include "vchiq.h" ++#include "vchiq_core.h" ++ ++#include "vchiq_util.h" ++ ++#include <stddef.h> ++ ++#if defined(__KERNEL__) ++#include <linux/module.h> ++#endif ++ ++#define vchiq_status_to_vchi(status) ((int32_t)status) ++ ++typedef struct { ++ VCHIQ_SERVICE_HANDLE_T handle; ++ ++ VCHIU_QUEUE_T queue; ++ ++ VCHI_CALLBACK_T callback; ++ void *callback_param; ++} SHIM_SERVICE_T; ++ ++/* ---------------------------------------------------------------------- ++ * return pointer to the mphi message driver function table ++ * -------------------------------------------------------------------- */ ++#ifdef WIN32 ++const VCHI_MESSAGE_DRIVER_T * ++mphi_get_func_table( void ) ++{ ++ return NULL; ++} ++#endif ++ ++/* ---------------------------------------------------------------------- ++ * return pointer to the mphi message driver function table ++ * -------------------------------------------------------------------- */ ++const VCHI_MESSAGE_DRIVER_T * ++vchi_mphi_message_driver_func_table( void ) ++{ ++ return NULL; ++} ++ ++/* ---------------------------------------------------------------------- ++ * return a pointer to the 'single' connection driver fops ++ * -------------------------------------------------------------------- */ ++const VCHI_CONNECTION_API_T * ++single_get_func_table( void ) ++{ ++ return NULL; ++} ++ ++VCHI_CONNECTION_T * vchi_create_connection( const VCHI_CONNECTION_API_T * function_table, ++ const VCHI_MESSAGE_DRIVER_T * low_level) ++{ ++ vcos_unused(function_table); ++ vcos_unused(low_level); ++ return NULL; ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_peek ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle, ++ * void **data, ++ * uint32_t *msg_size, ++ * VCHI_FLAGS_T flags ++ * ++ * Description: Routine to return a pointer to the current message (to allow in place processing) ++ * The message can be removed using vchi_msg_remove when you're finished ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_peek( VCHI_SERVICE_HANDLE_T handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags ) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_HEADER_T *header; ++ ++ vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); ++ ++ if (flags == VCHI_FLAGS_NONE) ++ if (vchiu_queue_is_empty(&service->queue)) ++ return -1; ++ ++ header = vchiu_queue_peek(&service->queue); ++ ++ *data = header->data; ++ *msg_size = header->size; ++ ++ return 0; ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_remove ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle, ++ * ++ * Description: Routine to remove a message (after it has been read with vchi_msg_peek) ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_remove( VCHI_SERVICE_HANDLE_T handle ) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_HEADER_T *header; ++ ++ header = vchiu_queue_pop(&service->queue); ++ ++ vchiq_release_message(service->handle, header); ++ ++ return 0; ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_queue ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * const void *data, ++ * uint32_t data_size, ++ * VCHI_FLAGS_T flags, ++ * void *msg_handle, ++ * ++ * Description: Thin wrapper to queue a message onto a connection ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle, ++ const void * data, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void * msg_handle ) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_ELEMENT_T element = {data, data_size}; ++ VCHIQ_STATUS_T status; ++ ++ vcos_unused(msg_handle); ++ ++ vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED); ++ ++ status = vchiq_queue_message(service->handle, &element, 1); ++ ++ // On some platforms, like linux kernel, vchiq_queue_message() may return ++ // VCHIQ_RETRY, so we need to implment a retry mechanism since this ++ // function is supposed to block until queued ++ while ( status == VCHIQ_RETRY ) ++ { ++ vcos_sleep( 1 ); ++ status = vchiq_queue_message(service->handle, &element, 1); ++ } ++ ++ return vchiq_status_to_vchi(status); ++} ++ ++/*********************************************************** ++ * Name: vchi_bulk_queue_receive ++ * ++ * Arguments: VCHI_BULK_HANDLE_T handle, ++ * void *data_dst, ++ * const uint32_t data_size, ++ * VCHI_FLAGS_T flags ++ * void *bulk_handle ++ * ++ * Description: Routine to setup a rcv buffer ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_bulk_queue_receive( VCHI_SERVICE_HANDLE_T handle, ++ void * data_dst, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void * bulk_handle ) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_BULK_MODE_T mode; ++ VCHIQ_STATUS_T status; ++ ++ switch ((int)flags) { ++ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ vcos_assert(service->callback); ++ mode = VCHIQ_BULK_MODE_CALLBACK; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: ++ mode = VCHIQ_BULK_MODE_BLOCKING; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ case VCHI_FLAGS_NONE: ++ mode = VCHIQ_BULK_MODE_NOCALLBACK; ++ break; ++ default: ++ vcos_assert(0); ++ return vchiq_status_to_vchi(VCHIQ_ERROR); ++ } ++ ++ status = vchiq_bulk_receive(service->handle, data_dst, data_size, ++ bulk_handle, mode); ++ ++ // On some platforms, like linux kernel, vchiq_bulk_receive() may return ++ // VCHIQ_RETRY, so we need to implment a retry mechanism since this ++ // function is supposed to block until queued ++ while ( status == VCHIQ_RETRY ) ++ { ++ vcos_sleep( 1 ); ++ status = vchiq_bulk_receive(service->handle, data_dst, data_size, ++ bulk_handle, mode); ++ } ++ ++ return vchiq_status_to_vchi(status); ++} ++ ++/*********************************************************** ++ * Name: vchi_bulk_queue_receive_reloc ++ * ++ * Arguments: VCHI_BULK_HANDLE_T handle, ++ * VCHI_MEM_HANDLE_T h ++ * uint32_t offset ++ * const uint32_t data_size, ++ * VCHI_FLAGS_T flags ++ * void *bulk_handle ++ * ++ * Description: Routine to setup a relocatable rcv buffer ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_bulk_queue_receive_reloc( const VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T h, ++ uint32_t offset, ++ uint32_t data_size, ++ const VCHI_FLAGS_T flags, ++ void * const bulk_handle ) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_BULK_MODE_T mode; ++ VCHIQ_STATUS_T status; ++ ++ switch ((int)flags) { ++ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ vcos_assert(service->callback); ++ mode = VCHIQ_BULK_MODE_CALLBACK; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: ++ mode = VCHIQ_BULK_MODE_BLOCKING; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ case VCHI_FLAGS_NONE: ++ mode = VCHIQ_BULK_MODE_NOCALLBACK; ++ break; ++ default: ++ vcos_assert(0); ++ return vchiq_status_to_vchi(VCHIQ_ERROR); ++ } ++ ++ status = vchiq_bulk_receive_handle(service->handle, h, (void*)offset, ++ data_size, bulk_handle, mode); ++ ++ // On some platforms, like linux kernel, vchiq_bulk_receive_handle() may ++ // return VCHIQ_RETRY, so we need to implment a retry mechanism since ++ // this function is supposed to block until queued ++ while ( status == VCHIQ_RETRY ) ++ { ++ vcos_sleep( 1 ); ++ status = vchiq_bulk_receive_handle(service->handle, h, (void*)offset, ++ data_size, bulk_handle, mode); ++ } ++ ++ return vchiq_status_to_vchi(status); ++} ++ ++/*********************************************************** ++ * Name: vchi_bulk_queue_transmit ++ * ++ * Arguments: VCHI_BULK_HANDLE_T handle, ++ * const void *data_src, ++ * uint32_t data_size, ++ * VCHI_FLAGS_T flags, ++ * void *bulk_handle ++ * ++ * Description: Routine to transmit some data ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_bulk_queue_transmit( VCHI_SERVICE_HANDLE_T handle, ++ const void * data_src, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void * bulk_handle ) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_BULK_MODE_T mode; ++ VCHIQ_STATUS_T status; ++ ++ switch ((int)flags) { ++ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ vcos_assert(service->callback); ++ mode = VCHIQ_BULK_MODE_CALLBACK; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ: ++ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: ++ mode = VCHIQ_BULK_MODE_BLOCKING; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ case VCHI_FLAGS_NONE: ++ mode = VCHIQ_BULK_MODE_NOCALLBACK; ++ break; ++ default: ++ vcos_assert(0); ++ return vchiq_status_to_vchi(VCHIQ_ERROR); ++ } ++ ++ status = vchiq_bulk_transmit(service->handle, data_src, data_size, ++ bulk_handle, mode); ++ ++ // On some platforms, like linux kernel, vchiq_bulk_transmit() may return ++ // VCHIQ_RETRY, so we need to implment a retry mechanism since this ++ // function is supposed to block until queued ++ while ( status == VCHIQ_RETRY ) ++ { ++ vcos_sleep( 1 ); ++ status = vchiq_bulk_transmit(service->handle, data_src, data_size, ++ bulk_handle, mode); ++ } ++ ++ return vchiq_status_to_vchi(status); ++} ++ ++/*********************************************************** ++ * Name: vchi_bulk_queue_transmit_reloc ++ * ++ * Arguments: VCHI_BULK_HANDLE_T handle, ++ * VCHI_MEM_HANDLE_T h_src, ++ * uint32_t offset, ++ * uint32_t data_size, ++ * VCHI_FLAGS_T flags, ++ * void *bulk_handle ++ * ++ * Description: Routine to transmit some data from a relocatable buffer ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++ ++int32_t vchi_bulk_queue_transmit_reloc( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T h_src, ++ uint32_t offset, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void * const bulk_handle ) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_BULK_MODE_T mode; ++ VCHIQ_STATUS_T status; ++ ++ switch ((int)flags) { ++ case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ vcos_assert(service->callback); ++ mode = VCHIQ_BULK_MODE_CALLBACK; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_DATA_READ: ++ case VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE: ++ mode = VCHIQ_BULK_MODE_BLOCKING; ++ break; ++ case VCHI_FLAGS_BLOCK_UNTIL_QUEUED: ++ case VCHI_FLAGS_NONE: ++ mode = VCHIQ_BULK_MODE_NOCALLBACK; ++ break; ++ default: ++ vcos_assert(0); ++ return vchiq_status_to_vchi(VCHIQ_ERROR); ++ } ++ ++ status = vchiq_bulk_transmit_handle(service->handle, h_src, (void*)offset, ++ data_size, bulk_handle, mode); ++ ++ // On some platforms, like linux kernel, vchiq_bulk_transmit_handle() may ++ // return VCHIQ_RETRY, so we need to implment a retry mechanism since this ++ // function is supposed to block until queued ++ while ( status == VCHIQ_RETRY ) ++ { ++ vcos_sleep( 1 ); ++ status = vchiq_bulk_transmit_handle(service->handle, h_src, (void*)offset, ++ data_size, bulk_handle, mode); ++ } ++ ++ return vchiq_status_to_vchi(status); ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_dequeue ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * void *data, ++ * uint32_t max_data_size_to_read, ++ * uint32_t *actual_msg_size ++ * VCHI_FLAGS_T flags ++ * ++ * Description: Routine to dequeue a message into the supplied buffer ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T handle, ++ void *data, ++ uint32_t max_data_size_to_read, ++ uint32_t *actual_msg_size, ++ VCHI_FLAGS_T flags ) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_HEADER_T *header; ++ ++ vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); ++ ++ if (flags == VCHI_FLAGS_NONE) ++ if (vchiu_queue_is_empty(&service->queue)) ++ return -1; ++ ++ header = vchiu_queue_pop(&service->queue); ++ ++ memcpy(data, header->data, header->size < max_data_size_to_read ? header->size : max_data_size_to_read); ++ ++ *actual_msg_size = header->size; ++ ++ vchiq_release_message(service->handle, header); ++ ++ return 0; ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_queuev ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * const void *data, ++ * uint32_t data_size, ++ * VCHI_FLAGS_T flags, ++ * void *msg_handle ++ * ++ * Description: Thin wrapper to queue a message onto a connection ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++ ++vcos_static_assert(sizeof(VCHI_MSG_VECTOR_T) == sizeof(VCHIQ_ELEMENT_T)); ++vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_base) == offsetof(VCHIQ_ELEMENT_T, data)); ++vcos_static_assert(offsetof(VCHI_MSG_VECTOR_T, vec_len) == offsetof(VCHIQ_ELEMENT_T, size)); ++ ++int32_t vchi_msg_queuev( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_VECTOR_T * vector, ++ uint32_t count, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ ++ vcos_unused(msg_handle); ++ ++ vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED); ++ ++ return vchiq_status_to_vchi(vchiq_queue_message(service->handle, (const VCHIQ_ELEMENT_T *)vector, count)); ++} ++ ++#ifdef USE_MEMMGR ++ ++/*********************************************************** ++ * Name: vchi_msg_queuev_ex ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * VCHI_MSG_VECTOR_EX_T *vector ++ * uint32_t count ++ * VCHI_FLAGS_T flags, ++ * void *msg_handle ++ * ++ * Description: Thin wrapper to queue an array of messages onto a connection ++ * Supports resolving MEM_HANDLE's at last possible moment to avoid deadlocks. ++ * ++ * Currently just a shim, so deadlocks are still possible! ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_queuev_ex( const VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_VECTOR_EX_T * const vector, ++ const uint32_t count, ++ const VCHI_FLAGS_T flags, ++ void * const msg_handle ) ++{ ++ int32_t success = -1; ++ // For now, we don't actually support sending anything other than ++ // a pointer, so handles have to be patched up; this is likely ++ // to cause deadlocks. This code is not designed to be either ++ // pretty, efficient, or deadlock-free. ++ ++ #define max_vecs 16 ++ VCHI_MSG_VECTOR_T copy[max_vecs]; ++ const uint8_t *orig[max_vecs]; ++ ++ int i; ++ vcos_unused(msg_handle); ++ ++ if (count > sizeof(copy)/sizeof(copy[0])) ++ { ++ vcos_assert(0); ++ return -1; ++ } ++ ++ for (i=0; i<count; i++) ++ { ++ VCHI_MSG_VECTOR_EX_T *v = vector+i; ++ ++ switch (vector[i].type) ++ { ++ case VCHI_VEC_POINTER: ++ copy[i].vec_base = v->u.ptr.vec_base; ++ copy[i].vec_len = v->u.ptr.vec_len; ++ break; ++ case VCHI_VEC_HANDLE: ++ vcos_assert(v->u.handle.offset+v->u.handle.vec_len <= mem_get_size(v->u.handle.handle)); ++ copy[i].vec_base = (uint8_t*)mem_lock(v->u.handle.handle) + v->u.handle.offset; ++ orig[i] = copy[i].vec_base; ++ copy[i].vec_len = v->u.handle.vec_len; ++ break; ++ case VCHI_VEC_LIST: ++ vcos_assert(0); // FIXME: implement this ++ break; ++ default: ++ vcos_assert(0); ++ } ++ } ++ success = vchi_msg_queuev( handle, ++ copy, ++ count, ++ flags &~ VCHI_FLAGS_INTERNAL, ++ msg_handle ); ++ if (vcos_verify(success == 0)) ++ { ++ // now we need to patch up the vectors if any have been only partially consumed, and ++ // unlock memory handles. ++ ++ for (i=0; i<count; i++) ++ { ++ VCHI_MSG_VECTOR_EX_T *v = vector+i; ++ ++ switch (vector[i].type) ++ { ++ case VCHI_VEC_POINTER: ++ if (flags & VCHI_FLAGS_ALLOW_PARTIAL) ++ { ++ v->u.ptr.vec_base = copy[i].vec_base; ++ v->u.ptr.vec_len = copy[i].vec_len; ++ } ++ break; ++ case VCHI_VEC_HANDLE: ++ mem_unlock(v->u.handle.handle); ++ if (flags & VCHI_FLAGS_ALLOW_PARTIAL) ++ { ++ const uint8_t *old = orig[i]; ++ uint32_t change = (const uint8_t*)copy[i].vec_base-old; ++ v->u.handle.offset += change; ++ v->u.handle.vec_len -= change; ++ } ++ break; ++ default: ++ vcos_assert(0); ++ } ++ } ++ } ++ ++ return vchiq_status_to_vchi(success); ++} ++ ++#endif ++ ++/*********************************************************** ++ * Name: vchi_held_msg_release ++ * ++ * Arguments: VCHI_HELD_MSG_T *message ++ * ++ * Description: Routine to release a held message (after it has been read with vchi_msg_hold) ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_held_msg_release( VCHI_HELD_MSG_T *message ) ++{ ++ vchiq_release_message((VCHIQ_SERVICE_HANDLE_T)message->service, (VCHIQ_HEADER_T *)message->message); ++ ++ return 0; ++} ++ ++/*********************************************************** ++ * Name: vchi_msg_hold ++ * ++ * Arguments: VCHI_SERVICE_HANDLE_T handle, ++ * void **data, ++ * uint32_t *msg_size, ++ * VCHI_FLAGS_T flags, ++ * VCHI_HELD_MSG_T *message_handle ++ * ++ * Description: Routine to return a pointer to the current message (to allow in place processing) ++ * The message is dequeued - don't forget to release the message using ++ * vchi_held_msg_release when you're finished ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++int32_t vchi_msg_hold( VCHI_SERVICE_HANDLE_T handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags, ++ VCHI_HELD_MSG_T *message_handle ) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ VCHIQ_HEADER_T *header; ++ ++ vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); ++ ++ if (flags == VCHI_FLAGS_NONE) ++ if (vchiu_queue_is_empty(&service->queue)) ++ return -1; ++ ++ header = vchiu_queue_pop(&service->queue); ++ ++ *data = header->data; ++ *msg_size = header->size; ++ ++ message_handle->service = (struct opaque_vchi_service_t *)service->handle; ++ message_handle->message = header; ++ ++ return 0; ++} ++ ++/*********************************************************** ++ * Name: vchi_initialise ++ * ++ * Arguments: VCHI_INSTANCE_T *instance_handle ++ * VCHI_CONNECTION_T **connections ++ * const uint32_t num_connections ++ * ++ * Description: Initialises the hardware but does not transmit anything ++ * When run as a Host App this will be called twice hence the need ++ * to malloc the state information ++ * ++ * Returns: 0 if successful, failure otherwise ++ * ++ ***********************************************************/ ++ ++int32_t vchi_initialise( VCHI_INSTANCE_T *instance_handle ) ++{ ++ VCHIQ_INSTANCE_T instance; ++ VCHIQ_STATUS_T status; ++ ++ status = vchiq_initialise(&instance); ++ ++ *instance_handle = (VCHI_INSTANCE_T)instance; ++ ++ return vchiq_status_to_vchi(status); ++} ++ ++/*********************************************************** ++ * Name: vchi_connect ++ * ++ * Arguments: VCHI_CONNECTION_T **connections ++ * const uint32_t num_connections ++ * VCHI_INSTANCE_T instance_handle ) ++ * ++ * Description: Starts the command service on each connection, ++ * causing INIT messages to be pinged back and forth ++ * ++ * Returns: 0 if successful, failure otherwise ++ * ++ ***********************************************************/ ++int32_t vchi_connect( VCHI_CONNECTION_T **connections, ++ const uint32_t num_connections, ++ VCHI_INSTANCE_T instance_handle ) ++{ ++ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; ++ ++ vcos_unused(connections); ++ vcos_unused(num_connections); ++ ++ return vchiq_connect(instance); ++} ++ ++ ++/*********************************************************** ++ * Name: vchi_disconnect ++ * ++ * Arguments: VCHI_INSTANCE_T instance_handle ++ * ++ * Description: Stops the command service on each connection, ++ * causing DE-INIT messages to be pinged back and forth ++ * ++ * Returns: 0 if successful, failure otherwise ++ * ++ ***********************************************************/ ++int32_t vchi_disconnect( VCHI_INSTANCE_T instance_handle ) ++{ ++ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; ++ return vchiq_status_to_vchi(vchiq_shutdown(instance)); ++} ++ ++ ++/*********************************************************** ++ * Name: vchi_service_open ++ * Name: vchi_service_create ++ * ++ * Arguments: VCHI_INSTANCE_T *instance_handle ++ * SERVICE_CREATION_T *setup, ++ * VCHI_SERVICE_HANDLE_T *handle ++ * ++ * Description: Routine to open a service ++ * ++ * Returns: int32_t - success == 0 ++ * ++ ***********************************************************/ ++ ++static VCHIQ_STATUS_T shim_callback(VCHIQ_REASON_T reason, VCHIQ_HEADER_T *header, VCHIQ_SERVICE_HANDLE_T handle, void *bulk_user) ++{ ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)VCHIQ_GET_SERVICE_USERDATA(handle); ++ ++ switch (reason) { ++ case VCHIQ_MESSAGE_AVAILABLE: ++ vchiu_queue_push(&service->queue, header); ++ ++ if (service->callback) ++ service->callback(service->callback_param, VCHI_CALLBACK_MSG_AVAILABLE, NULL); ++ break; ++ case VCHIQ_BULK_TRANSMIT_DONE: ++ if (service->callback) ++ service->callback(service->callback_param, VCHI_CALLBACK_BULK_SENT, bulk_user); ++ break; ++ case VCHIQ_BULK_RECEIVE_DONE: ++ if (service->callback) ++ service->callback(service->callback_param, VCHI_CALLBACK_BULK_RECEIVED, bulk_user); ++ break; ++ case VCHIQ_SERVICE_CLOSED: ++ if (service->callback) ++ service->callback(service->callback_param, VCHI_CALLBACK_SERVICE_CLOSED, NULL); ++ break; ++ case VCHIQ_SERVICE_OPENED: ++ /* No equivalent VCHI reason */ ++ break; ++ case VCHIQ_BULK_TRANSMIT_ABORTED: ++ if (service->callback) ++ service->callback(service->callback_param, VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, bulk_user); ++ break; ++ case VCHIQ_BULK_RECEIVE_ABORTED: ++ if (service->callback) ++ service->callback(service->callback_param, VCHI_CALLBACK_BULK_RECEIVE_ABORTED, bulk_user); ++ break; ++ default: ++ vcos_assert(0); ++ break; ++ } ++ ++ return VCHIQ_SUCCESS; ++} ++ ++static SHIM_SERVICE_T *service_alloc(VCHIQ_INSTANCE_T instance, ++ SERVICE_CREATION_T *setup) ++{ ++ SHIM_SERVICE_T *service = vcos_calloc(1, sizeof(SHIM_SERVICE_T), "vchiq_shim"); ++ ++ vcos_unused(instance); ++ ++ if (service) ++ { ++ if (vchiu_queue_init(&service->queue, 64)) ++ { ++ service->callback = setup->callback; ++ service->callback_param = setup->callback_param; ++ } ++ else ++ { ++ vcos_free(service); ++ service = NULL; ++ } ++ } ++ ++ return service; ++} ++ ++static void service_free(SHIM_SERVICE_T *service) ++{ ++ if (service) ++ { ++ vchiu_queue_delete(&service->queue); ++ vcos_free((void*)service); ++ } ++} ++ ++int32_t vchi_service_open( VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, ++ VCHI_SERVICE_HANDLE_T *handle) ++{ ++ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; ++ SHIM_SERVICE_T *service = service_alloc(instance, setup); ++ if (service) ++ { ++ VCHIQ_STATUS_T status = vchiq_open_service(instance, setup->service_id, shim_callback, service, &service->handle); ++ if (status != VCHIQ_SUCCESS) ++ { ++ service_free(service); ++ service = NULL; ++ } ++ } ++ ++ *handle = (VCHI_SERVICE_HANDLE_T)service; ++ ++ return (service != NULL) ? 0 : -1; ++} ++ ++int32_t vchi_service_create( VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, ++ VCHI_SERVICE_HANDLE_T *handle ) ++{ ++ VCHIQ_INSTANCE_T instance = (VCHIQ_INSTANCE_T)instance_handle; ++ SHIM_SERVICE_T *service = service_alloc(instance, setup); ++ if (service) ++ { ++ VCHIQ_STATUS_T status = vchiq_add_service(instance, setup->service_id, shim_callback, service, &service->handle); ++ if (status != VCHIQ_SUCCESS) ++ { ++ service_free(service); ++ service = NULL; ++ } ++ } ++ ++ *handle = (VCHI_SERVICE_HANDLE_T)service; ++ ++ return (service != NULL) ? 0 : -1; ++} ++ ++int32_t vchi_service_close( const VCHI_SERVICE_HANDLE_T handle ) ++{ ++ vcos_unused(handle); ++ ++ // YTI?? ++ return 0; ++} ++ ++/* ---------------------------------------------------------------------- ++ * read a uint32_t from buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++uint32_t ++vchi_readbuf_uint32( const void *_ptr ) ++{ ++ const unsigned char *ptr = _ptr; ++ return ptr[0] | (ptr[1] << 8) | (ptr[2] << 16) | (ptr[3] << 24); ++} ++ ++/* ---------------------------------------------------------------------- ++ * write a uint32_t to buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++void ++vchi_writebuf_uint32( void *_ptr, uint32_t value ) ++{ ++ unsigned char *ptr = _ptr; ++ ptr[0] = (unsigned char)((value >> 0) & 0xFF); ++ ptr[1] = (unsigned char)((value >> 8) & 0xFF); ++ ptr[2] = (unsigned char)((value >> 16) & 0xFF); ++ ptr[3] = (unsigned char)((value >> 24) & 0xFF); ++} ++ ++/* ---------------------------------------------------------------------- ++ * read a uint16_t from buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++uint16_t ++vchi_readbuf_uint16( const void *_ptr ) ++{ ++ const unsigned char *ptr = _ptr; ++ return ptr[0] | (ptr[1] << 8); ++} ++ ++/* ---------------------------------------------------------------------- ++ * write a uint16_t into the buffer. ++ * network format is defined to be little endian ++ * -------------------------------------------------------------------- */ ++void ++vchi_writebuf_uint16( void *_ptr, uint16_t value ) ++{ ++ unsigned char *ptr = _ptr; ++ ptr[0] = (value >> 0) & 0xFF; ++ ptr[1] = (value >> 8) & 0xFF; ++} ++ ++/*********************************************************** ++ * Name: vchi_service_use ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle ++ * ++ * Description: Routine to increment refcount on a service ++ * ++ * Returns: void ++ * ++ ***********************************************************/ ++int32_t vchi_service_use( const VCHI_SERVICE_HANDLE_T handle ) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if(service) ++ { ++ ret = vchiq_status_to_vchi(vchiq_use_service(service->handle)); ++ } ++ return ret; ++} ++ ++/*********************************************************** ++ * Name: vchi_service_release ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle ++ * ++ * Description: Routine to decrement refcount on a service ++ * ++ * Returns: void ++ * ++ ***********************************************************/ ++int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle ) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if(service) ++ { ++ ret = vchiq_status_to_vchi(vchiq_release_service(service->handle)); ++ } ++ return ret; ++} ++ ++#if defined(__KERNEL__) ++EXPORT_SYMBOL(vchi_initialise); ++EXPORT_SYMBOL(vchi_connect); ++EXPORT_SYMBOL(vchi_bulk_queue_transmit); ++EXPORT_SYMBOL(vchi_msg_dequeue); ++EXPORT_SYMBOL(vchi_msg_queue); ++EXPORT_SYMBOL(vchi_msg_queuev); ++EXPORT_SYMBOL(vchi_service_close); ++EXPORT_SYMBOL(vchi_service_open); ++EXPORT_SYMBOL(vchi_service_create); ++EXPORT_SYMBOL(vchi_service_use); ++EXPORT_SYMBOL(vchi_service_release); ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.c +@@ -0,0 +1,97 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include "vchiq_util.h" ++ ++#if !defined(__KERNEL__) ++#include <stdlib.h> ++#endif ++ ++static __inline int is_pow2(int i) ++{ ++ return i && !(i & (i - 1)); ++} ++ ++int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size) ++{ ++ vcos_assert(is_pow2(size)); ++ ++ queue->size = size; ++ queue->read = 0; ++ queue->write = 0; ++ ++ vcos_event_create(&queue->pop, "vchiu"); ++ vcos_event_create(&queue->push, "vchiu"); ++ ++ queue->storage = vcos_malloc(size * sizeof(VCHIQ_HEADER_T *), VCOS_FUNCTION); ++ if (queue->storage == NULL) ++ { ++ vchiu_queue_delete(queue); ++ return 0; ++ } ++ return 1; ++} ++ ++void vchiu_queue_delete(VCHIU_QUEUE_T *queue) ++{ ++ vcos_event_delete(&queue->pop); ++ vcos_event_delete(&queue->push); ++ if (queue->storage != NULL) ++ vcos_free(queue->storage); ++} ++ ++int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue) ++{ ++ return queue->read == queue->write; ++} ++ ++void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header) ++{ ++ while (queue->write == queue->read + queue->size) ++ vcos_event_wait(&queue->pop); ++ ++ queue->storage[queue->write & (queue->size - 1)] = header; ++ ++ queue->write++; ++ ++ vcos_event_signal(&queue->push); ++} ++ ++VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue) ++{ ++ while (queue->write == queue->read) ++ vcos_event_wait(&queue->push); ++ ++ return queue->storage[queue->read & (queue->size - 1)]; ++} ++ ++VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue) ++{ ++ VCHIQ_HEADER_T *header; ++ ++ while (queue->write == queue->read) ++ vcos_event_wait(&queue->push); ++ ++ header = queue->storage[queue->read & (queue->size - 1)]; ++ ++ queue->read++; ++ ++ vcos_event_signal(&queue->pop); ++ ++ return header; ++} +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_util.h +@@ -0,0 +1,47 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef VCHIQ_UTIL_H ++#define VCHIQ_UTIL_H ++ ++#include "vchiq_if.h" ++#include "interface/vcos/vcos.h" ++ ++typedef struct { ++ int size; ++ int read; ++ int write; ++ ++ VCOS_EVENT_T pop; ++ VCOS_EVENT_T push; ++ ++ VCHIQ_HEADER_T **storage; ++} VCHIU_QUEUE_T; ++ ++extern int vchiu_queue_init(VCHIU_QUEUE_T *queue, int size); ++extern void vchiu_queue_delete(VCHIU_QUEUE_T *queue); ++ ++extern int vchiu_queue_is_empty(VCHIU_QUEUE_T *queue); ++ ++extern void vchiu_queue_push(VCHIU_QUEUE_T *queue, VCHIQ_HEADER_T *header); ++ ++extern VCHIQ_HEADER_T *vchiu_queue_peek(VCHIU_QUEUE_T *queue); ++extern VCHIQ_HEADER_T *vchiu_queue_pop(VCHIU_QUEUE_T *queue); ++ ++#endif ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_cmd.c +@@ -0,0 +1,681 @@ ++/***************************************************************************** ++* Copyright 2009 - 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++/***************************************************************************** ++* ++* This file provides a generic command line interface which allows ++* vcos internals to be manipulated and/or displayed. ++* ++*****************************************************************************/ ++ ++/* ---- Include Files ---------------------------------------------------- */ ++ ++#include "interface/vcos/vcos.h" ++ ++#ifdef HAVE_VCOS_VERSION ++#include "interface/vcos/vcos_build_info.h" ++#endif ++ ++ #ifdef _VIDEOCORE ++#include vcfw/logging/logging.h ++#endif ++ ++/* ---- Public Variables ------------------------------------------------- */ ++ ++/* ---- Private Constants and Types -------------------------------------- */ ++ ++#define VCOS_LOG_CATEGORY (&vcos_cmd_log_category) ++VCOS_LOG_CAT_T vcos_cmd_log_category; ++ ++/* ---- Private Variables ------------------------------------------------ */ ++ ++static struct VCOS_CMD_GLOBALS_T ++{ ++ VCOS_MUTEX_T lock; ++ VCOS_ONCE_T initialized; ++ ++ unsigned num_cmd_entries; ++ unsigned num_cmd_alloc; ++ VCOS_CMD_T *cmd_entry; ++ ++ VCOS_LOG_CAT_T *log_category; ++} cmd_globals; ++ ++/* ---- Private Function Prototypes -------------------------------------- */ ++ ++static VCOS_STATUS_T help_cmd( VCOS_CMD_PARAM_T *param ); ++ ++/* ---- Functions ------------------------------------------------------- */ ++ ++/***************************************************************************** ++* ++* Walks through the commands looking for a particular command ++* ++*****************************************************************************/ ++ ++static VCOS_CMD_T *find_cmd( VCOS_CMD_T *cmd_entry, const char *name ) ++{ ++ VCOS_CMD_T *scan_entry = cmd_entry; ++ ++ while ( scan_entry->name != NULL ) ++ { ++ if ( vcos_strcmp( scan_entry->name, name ) == 0 ) ++ { ++ return scan_entry; ++ } ++ scan_entry++; ++ } ++ ++ return NULL; ++} ++ ++/***************************************************************************** ++* ++* Saves away ++* each line individually. ++* ++*****************************************************************************/ ++ ++void vcos_cmd_always_log_output( VCOS_LOG_CAT_T *log_category ) ++{ ++ cmd_globals.log_category = log_category; ++} ++ ++/***************************************************************************** ++* ++* Walks through a buffer containing newline separated lines, and logs ++* each line individually. ++* ++*****************************************************************************/ ++ ++static void cmd_log_results( VCOS_CMD_PARAM_T *param ) ++{ ++ char *start; ++ char *end; ++ ++ start = end = param->result_buf; ++ ++ while ( *start != '\0' ) ++ { ++ while (( *end != '\0' ) && ( *end != '\n' )) ++ end++; ++ ++ if ( *end == '\n' ) ++ { ++ *end++ = '\0'; ++ } ++ ++ if ( cmd_globals.log_category != NULL ) ++ { ++ if ( vcos_is_log_enabled( cmd_globals.log_category, VCOS_LOG_INFO )) ++ { ++ vcos_log_impl( cmd_globals.log_category, VCOS_LOG_INFO, "%s", start ); ++ } ++ } ++ else ++ { ++ vcos_log_info( "%s", start ); ++ } ++ ++ start = end; ++ } ++ ++ /* Since we logged the buffer, reset the pointer back to the beginning. */ ++ ++ param->result_ptr = param->result_buf; ++ param->result_buf[0] = '\0'; ++} ++ ++/***************************************************************************** ++* ++* Since we may have limited output space, we create a generic routine ++* which tries to use the result space, but will switch over to using ++* logging if the output is too large. ++* ++*****************************************************************************/ ++ ++void vcos_cmd_vprintf( VCOS_CMD_PARAM_T *param, const char *fmt, va_list args ) ++{ ++ int bytes_written; ++ int bytes_remaining; ++ ++ bytes_remaining = (int)(param->result_size - ( param->result_ptr - param->result_buf )); ++ ++ bytes_written = vcos_vsnprintf( param->result_ptr, bytes_remaining, fmt, args ); ++ ++ if ( cmd_globals.log_category != NULL ) ++ { ++ /* We're going to log each line as we encounter it. If the buffer ++ * doesn't end in a newline, then we'll wait for one first. ++ */ ++ ++ if ( (( bytes_written + 1 ) >= bytes_remaining ) ++ || ( param->result_ptr[ bytes_written - 1 ] == '\n' )) ++ { ++ cmd_log_results( param ); ++ } ++ else ++ { ++ param->result_ptr += bytes_written; ++ } ++ } ++ else ++ { ++ if (( bytes_written + 1 ) >= bytes_remaining ) ++ { ++ /* Output doesn't fit - switch over to logging */ ++ ++ param->use_log = 1; ++ ++ *param->result_ptr = '\0'; /* Zap the partial line that didn't fit above. */ ++ ++ cmd_log_results( param ); /* resets result_ptr */ ++ ++ bytes_written = vcos_vsnprintf( param->result_ptr, bytes_remaining, fmt, args ); ++ } ++ param->result_ptr += bytes_written; ++ } ++} ++ ++/***************************************************************************** ++* ++* Prints the output. ++* ++*****************************************************************************/ ++ ++void vcos_cmd_printf( VCOS_CMD_PARAM_T *param, const char *fmt, ... ) ++{ ++ va_list args; ++ ++ va_start( args, fmt ); ++ vcos_cmd_vprintf( param, fmt, args ); ++ va_end( args ); ++} ++ ++/***************************************************************************** ++* ++* Prints the arguments which were on the command line prior to ours. ++* ++*****************************************************************************/ ++ ++static void print_argument_prefix( VCOS_CMD_PARAM_T *param ) ++{ ++ int arg_idx; ++ ++ for ( arg_idx = 0; ¶m->argv_orig[arg_idx] != param->argv; arg_idx++ ) ++ { ++ vcos_cmd_printf( param, "%s ", param->argv_orig[arg_idx] ); ++ } ++} ++ ++/***************************************************************************** ++* ++* Prints an error message, prefixed by the command chain required to get ++* to where we're at. ++* ++*****************************************************************************/ ++ ++void vcos_cmd_error( VCOS_CMD_PARAM_T *param, const char *fmt, ... ) ++{ ++ va_list args; ++ ++ print_argument_prefix( param ); ++ ++ va_start( args, fmt ); ++ vcos_cmd_vprintf( param, fmt, args ); ++ va_end( args ); ++ vcos_cmd_printf( param, "\n" ); ++} ++ ++/**************************************************************************** ++* ++* usage - prints command usage for an array of commands. ++* ++***************************************************************************/ ++ ++static void usage( VCOS_CMD_PARAM_T *param, VCOS_CMD_T *cmd_entry ) ++{ ++ int cmd_idx; ++ int nameWidth = 0; ++ int argsWidth = 0; ++ VCOS_CMD_T *scan_entry; ++ ++ vcos_cmd_printf( param, "Usage: " ); ++ print_argument_prefix( param ); ++ vcos_cmd_printf( param, "command [args ...]\n" ); ++ vcos_cmd_printf( param, "\n" ); ++ vcos_cmd_printf( param, "Where command is one of the following:\n" ); ++ ++ for ( cmd_idx = 0; cmd_entry[cmd_idx].name != NULL; cmd_idx++ ) ++ { ++ int aw; ++ int nw; ++ ++ scan_entry = &cmd_entry[cmd_idx]; ++ ++ nw = vcos_strlen( scan_entry->name ); ++ aw = vcos_strlen( scan_entry->args ); ++ ++ if ( nw > nameWidth ) ++ { ++ nameWidth = nw; ++ } ++ if ( aw > argsWidth ) ++ { ++ argsWidth = aw; ++ } ++ } ++ ++ for ( cmd_idx = 0; cmd_entry[cmd_idx].name != NULL; cmd_idx++ ) ++ { ++ scan_entry = &cmd_entry[cmd_idx]; ++ ++ vcos_cmd_printf( param, " %-*s %-*s - %s\n", ++ nameWidth, scan_entry->name, ++ argsWidth, scan_entry->args, ++ scan_entry->descr ); ++ } ++} ++ ++/**************************************************************************** ++* ++* Prints the usage for the current command. ++* ++***************************************************************************/ ++ ++void vcos_cmd_usage( VCOS_CMD_PARAM_T *param ) ++{ ++ VCOS_CMD_T *cmd_entry; ++ ++ cmd_entry = param->cmd_entry; ++ ++ if ( cmd_entry->sub_cmd_entry != NULL ) ++ { ++ /* This command is command with sub-commands */ ++ ++ usage( param, param->cmd_entry->sub_cmd_entry ); ++ } ++ else ++ { ++ vcos_cmd_printf( param, "Usage: " ); ++ print_argument_prefix( param ); ++ vcos_cmd_printf( param, "%s - %s\n", ++ param->cmd_entry->args, ++ param->cmd_entry->descr ); ++ } ++} ++ ++/***************************************************************************** ++* ++* Command to print out the help ++* ++* This help command is only called from the main menu. ++* ++*****************************************************************************/ ++ ++static VCOS_STATUS_T help_cmd( VCOS_CMD_PARAM_T *param ) ++{ ++ VCOS_CMD_T *found_entry; ++ ++#if 0 ++ { ++ int arg_idx; ++ ++ vcos_log_trace( "%s: argc = %d", __func__, param->argc ); ++ for ( arg_idx = 0; arg_idx < param->argc; arg_idx++ ) ++ { ++ vcos_log_trace( "%s: argv[%d] = '%s'", __func__, arg_idx, param->argv[arg_idx] ); ++ } ++ } ++#endif ++ ++ /* If there is an argument after the word help, then we want to print ++ * help for that command. ++ */ ++ ++ if ( param->argc == 1 ) ++ { ++ if ( param->cmd_parent_entry == cmd_globals.cmd_entry ) ++ { ++ /* Bare help - print the command usage for the root */ ++ ++ usage( param, cmd_globals.cmd_entry ); ++ return VCOS_SUCCESS; ++ } ++ ++ /* For all other cases help requires an argument */ ++ ++ vcos_cmd_error( param, "%s requires an argument", param->argv[0] ); ++ return VCOS_EINVAL; ++ } ++ ++ /* We were given an argument. */ ++ ++ if (( found_entry = find_cmd( param->cmd_parent_entry, param->argv[1] )) != NULL ) ++ { ++ /* Make it look like the command that was specified is the one that's ++ * currently running ++ */ ++ ++ param->cmd_entry = found_entry; ++ param->argv[0] = param->argv[1]; ++ param->argv++; ++ param->argc--; ++ ++ vcos_cmd_usage( param ); ++ return VCOS_SUCCESS; ++ } ++ ++ vcos_cmd_error( param, "- unrecognized command: '%s'", param->argv[1] ); ++ return VCOS_ENOENT; ++} ++ ++/***************************************************************************** ++* ++* Command to print out the version/build information. ++* ++*****************************************************************************/ ++ ++#ifdef HAVE_VCOS_VERSION ++ ++static VCOS_STATUS_T version_cmd( VCOS_CMD_PARAM_T *param ) ++{ ++ static const char* copyright = "Copyright (c) 2011 Broadcom"; ++ ++ vcos_cmd_printf( param, "%s %s\n%s\nversion %s\n", ++ vcos_get_build_date(), ++ vcos_get_build_time(), ++ copyright, ++ vcos_get_build_version() ); ++ ++ return VCOS_SUCCESS; ++} ++ ++#endif ++ ++/***************************************************************************** ++* ++* Internal commands ++* ++*****************************************************************************/ ++ ++static VCOS_CMD_T cmd_help = { "help", "[command]", help_cmd, NULL, "Prints command help information" }; ++ ++#ifdef HAVE_VCOS_VERSION ++static VCOS_CMD_T cmd_version = { "version", "", version_cmd, NULL, "Prints build/version information" }; ++#endif ++ ++/***************************************************************************** ++* ++* Walks the command table and executes the commands ++* ++*****************************************************************************/ ++ ++static VCOS_STATUS_T execute_cmd( VCOS_CMD_PARAM_T *param, VCOS_CMD_T *cmd_entry ) ++{ ++ const char *cmdStr; ++ VCOS_CMD_T *found_entry; ++ ++#if 0 ++ { ++ int arg_idx; ++ ++ vcos_cmd_printf( param, "%s: argc = %d", __func__, param->argc ); ++ for ( arg_idx = 0; arg_idx < param->argc; arg_idx++ ) ++ { ++ vcos_cmd_printf( param, " argv[%d] = '%s'", arg_idx, param->argv[arg_idx] ); ++ } ++ vcos_cmd_printf( param, "\n" ); ++ } ++#endif ++ ++ if ( param->argc <= 1 ) ++ { ++ /* No command specified */ ++ ++ vcos_cmd_error( param, "%s - no command specified", param->argv[0] ); ++ return VCOS_EINVAL; ++ } ++ ++ /* argv[0] is the command/program that caused us to get invoked, so we strip ++ * it off. ++ */ ++ ++ param->argc--; ++ param->argv++; ++ param->cmd_parent_entry = cmd_entry; ++ ++ /* Not the help command, scan for the command and execute it. */ ++ ++ cmdStr = param->argv[0]; ++ ++ if (( found_entry = find_cmd( cmd_entry, cmdStr )) != NULL ) ++ { ++ if ( found_entry->sub_cmd_entry != NULL ) ++ { ++ return execute_cmd( param, found_entry->sub_cmd_entry ); ++ } ++ ++ param->cmd_entry = found_entry; ++ return found_entry->cmd_fn( param ); ++ } ++ ++ /* Unrecognized command - check to see if it was the help command */ ++ ++ if ( vcos_strcmp( cmdStr, cmd_help.name ) == 0 ) ++ { ++ return help_cmd( param ); ++ } ++ ++ vcos_cmd_error( param, "- unrecognized command: '%s'", cmdStr ); ++ return VCOS_ENOENT; ++} ++ ++/***************************************************************************** ++* ++* Initializes the command line parser. ++* ++*****************************************************************************/ ++ ++static void vcos_cmd_init( void ) ++{ ++ vcos_mutex_create( &cmd_globals.lock, "vcos_cmd" ); ++ ++ cmd_globals.num_cmd_entries = 0; ++ cmd_globals.num_cmd_alloc = 0; ++ cmd_globals.cmd_entry = NULL; ++} ++ ++/***************************************************************************** ++* ++* Command line processor. ++* ++*****************************************************************************/ ++ ++VCOS_STATUS_T vcos_cmd_execute( int argc, char **argv, size_t result_size, char *result_buf ) ++{ ++ VCOS_STATUS_T rc = VCOS_EINVAL; ++ VCOS_CMD_PARAM_T param; ++ ++ vcos_once( &cmd_globals.initialized, vcos_cmd_init ); ++ ++ param.argc = argc; ++ param.argv = param.argv_orig = argv; ++ ++ param.use_log = 0; ++ param.result_size = result_size; ++ param.result_ptr = result_buf; ++ param.result_buf = result_buf; ++ ++ result_buf[0] = '\0'; ++ ++ vcos_mutex_lock( &cmd_globals.lock ); ++ ++ rc = execute_cmd( ¶m, cmd_globals.cmd_entry ); ++ ++ if ( param.use_log ) ++ { ++ cmd_log_results( ¶m ); ++ vcos_snprintf( result_buf, result_size, "results logged" ); ++ } ++ else ++ if ( cmd_globals.log_category != NULL ) ++ { ++ if ( result_buf[0] != '\0' ) ++ { ++ /* There is a partial line still buffered. */ ++ ++ vcos_cmd_printf( ¶m, "\n" ); ++ } ++ } ++ ++ vcos_mutex_unlock( &cmd_globals.lock ); ++ ++ return rc; ++} ++ ++/***************************************************************************** ++* ++* Registers a command entry with the command line processor ++* ++*****************************************************************************/ ++ ++VCOS_STATUS_T vcos_cmd_register( VCOS_CMD_T *cmd_entry ) ++{ ++ VCOS_STATUS_T rc; ++ VCOS_UNSIGNED new_num_cmd_alloc; ++ VCOS_CMD_T *new_cmd_entry; ++ VCOS_CMD_T *old_cmd_entry; ++ VCOS_CMD_T *scan_entry; ++ ++ vcos_once( &cmd_globals.initialized, vcos_cmd_init ); ++ ++ vcos_assert( cmd_entry != NULL ); ++ vcos_assert( cmd_entry->name != NULL ); ++ ++ vcos_log_trace( "%s: cmd '%s'", __FUNCTION__, cmd_entry->name ); ++ ++ vcos_assert( cmd_entry->args != NULL ); ++ vcos_assert(( cmd_entry->cmd_fn != NULL ) || ( cmd_entry->sub_cmd_entry != NULL )); ++ vcos_assert( cmd_entry->descr != NULL ); ++ ++ /* We expect vcos_cmd_init to be called before vcos_logging_init, so we ++ * need to defer registering our logging category until someplace ++ * like right here. ++ */ ++ ++ if ( vcos_cmd_log_category.name == NULL ) ++ { ++ /* ++ * If you're using the command interface, you pretty much always want ++ * log messages from this file to show up. So we change the default ++ * from ERROR to be the more reasonable INFO level. ++ */ ++ ++ vcos_log_set_level(&vcos_cmd_log_category, VCOS_LOG_INFO); ++ vcos_log_register("vcos_cmd", &vcos_cmd_log_category); ++ ++ /* We register a help command so that it shows up in the usage. */ ++ ++ vcos_cmd_register( &cmd_help ); ++#ifdef HAVE_VCOS_VERSION ++ vcos_cmd_register( &cmd_version ); ++#endif ++ } ++ ++ vcos_mutex_lock( &cmd_globals.lock ); ++ ++ if ( cmd_globals.num_cmd_entries >= cmd_globals.num_cmd_alloc ) ++ { ++ if ( cmd_globals.num_cmd_alloc == 0 ) ++ { ++ /* We haven't allocated a table yet */ ++ } ++ ++ /* The number 8 is rather arbitrary. */ ++ ++ new_num_cmd_alloc = cmd_globals.num_cmd_alloc + 8; ++ ++ /* The + 1 is to ensure that we always have a NULL entry at the end. */ ++ ++ new_cmd_entry = (VCOS_CMD_T *)vcos_calloc( new_num_cmd_alloc + 1, sizeof( *cmd_entry ), "vcos_cmd_entries" ); ++ if ( new_cmd_entry == NULL ) ++ { ++ rc = VCOS_ENOMEM; ++ goto out; ++ } ++ memcpy( new_cmd_entry, cmd_globals.cmd_entry, cmd_globals.num_cmd_entries * sizeof( *cmd_entry )); ++ cmd_globals.num_cmd_alloc = new_num_cmd_alloc; ++ old_cmd_entry = cmd_globals.cmd_entry; ++ cmd_globals.cmd_entry = new_cmd_entry; ++ vcos_free( old_cmd_entry ); ++ } ++ ++ if ( cmd_globals.num_cmd_entries == 0 ) ++ { ++ /* This is the first command being registered */ ++ ++ cmd_globals.cmd_entry[0] = *cmd_entry; ++ } ++ else ++ { ++ /* Keep the list in alphabetical order. We start at the end and work backwards ++ * shuffling entries up one until we find an insertion point. ++ */ ++ ++ for ( scan_entry = &cmd_globals.cmd_entry[cmd_globals.num_cmd_entries - 1]; ++ scan_entry >= cmd_globals.cmd_entry; scan_entry-- ) ++ { ++ if ( vcos_strcmp( cmd_entry->name, scan_entry->name ) > 0 ) ++ { ++ /* We found an insertion point. */ ++ ++ break; ++ } ++ ++ scan_entry[1] = scan_entry[0]; ++ } ++ scan_entry[1] = *cmd_entry; ++ } ++ cmd_globals.num_cmd_entries++; ++ ++ rc = VCOS_SUCCESS; ++ ++out: ++ ++ vcos_mutex_unlock( &cmd_globals.lock ); ++ return rc; ++} ++ ++/***************************************************************************** ++* ++* Registers multiple commands. ++* ++*****************************************************************************/ ++ ++VCOS_STATUS_T vcos_cmd_register_multiple( VCOS_CMD_T *cmd_entry ) ++{ ++ VCOS_STATUS_T status; ++ ++ while ( cmd_entry->name != NULL ) ++ { ++ if (( status = vcos_cmd_register( cmd_entry )) != VCOS_SUCCESS ) ++ { ++ return status; ++ } ++ cmd_entry++; ++ } ++ return VCOS_SUCCESS; ++} ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_common.h +@@ -0,0 +1,76 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - common postamble code ++=============================================================================*/ ++ ++/** \file ++ * ++ * Postamble code included by the platform-specific header files ++ */ ++ ++#define VCOS_THREAD_PRI_DEFAULT VCOS_THREAD_PRI_NORMAL ++ ++#if !defined(VCOS_THREAD_PRI_INCREASE) ++#error Which way to thread priorities go? ++#endif ++ ++#if VCOS_THREAD_PRI_INCREASE < 0 ++/* smaller numbers are higher priority */ ++#define VCOS_THREAD_PRI_LESS(x) ((x)<VCOS_THREAD_PRI_MAX?(x)+1:VCOS_THREAD_PRI_MAX) ++#define VCOS_THREAD_PRI_MORE(x) ((x)>VCOS_THREAD_PRI_MIN?(x)-1:VCOS_THREAD_PRI_MIN) ++#else ++/* bigger numbers are lower priority */ ++#define VCOS_THREAD_PRI_MORE(x) ((x)<VCOS_THREAD_PRI_MAX?(x)+1:VCOS_THREAD_PRI_MAX) ++#define VCOS_THREAD_PRI_LESS(x) ((x)>VCOS_THREAD_PRI_MIN?(x)-1:VCOS_THREAD_PRI_MIN) ++#endif ++ ++/* Convenience for Brits: */ ++#define VCOS_APPLICATION_INITIALISE VCOS_APPLICATION_INITIALIZE ++ ++/* ++ * Check for constant definitions ++ */ ++#ifndef VCOS_TICKS_PER_SECOND ++#error VCOS_TICKS_PER_SECOND not defined ++#endif ++ ++#if !defined(VCOS_THREAD_PRI_MIN) || !defined(VCOS_THREAD_PRI_MAX) ++#error Priority range not defined ++#endif ++ ++#if !defined(VCOS_THREAD_PRI_HIGHEST) || !defined(VCOS_THREAD_PRI_LOWEST) || !defined(VCOS_THREAD_PRI_NORMAL) ++#error Priority ordering not defined ++#endif ++ ++#if !defined(VCOS_CAN_SET_STACK_ADDR) ++#error Can stack addresses be set on this platform? Please set this macro to either 0 or 1. ++#endif ++ ++#if (_VCOS_AFFINITY_CPU0|_VCOS_AFFINITY_CPU1) & (~_VCOS_AFFINITY_MASK) ++#error _VCOS_AFFINITY_CPUxxx values are not consistent with _VCOS_AFFINITY_MASK ++#endif ++ ++/** Append to the end of a singly-linked queue, O(1). Works with ++ * any structure where list has members 'head' and 'tail' and ++ * item has a 'next' pointer. ++ */ ++#define VCOS_QUEUE_APPEND_TAIL(list, item) {\ ++ (item)->next = NULL;\ ++ if (!(list)->head) {\ ++ (list)->head = (list)->tail = (item); \ ++ } else {\ ++ (list)->tail->next = (item); \ ++ (list)->tail = (item); \ ++ } \ ++} ++ ++#ifndef VCOS_HAVE_TIMER ++VCOSPRE_ void VCOSPOST_ vcos_timer_init(void); ++#endif ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_blockpool.h +@@ -0,0 +1,260 @@ ++/*============================================================================= ++Copyright (c) 2011 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - event flags implemented via a semaphore ++=============================================================================*/ ++ ++#ifndef VCOS_GENERIC_BLOCKPOOL_H ++#define VCOS_GENERIC_BLOCKPOOL_H ++ ++/** ++ * \file ++ * ++ * This provides a generic, thread safe implementation of a VCOS block pool ++ * fixed size memory allocator. ++ */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++ ++/** Bits 0 to (VCOS_BLOCKPOOL_SUBPOOL_BITS - 1) are used to store the ++ * subpool id. */ ++#define VCOS_BLOCKPOOL_SUBPOOL_BITS 3 ++#define VCOS_BLOCKPOOL_MAX_SUBPOOLS (1 << VCOS_BLOCKPOOL_SUBPOOL_BITS) ++ ++/* Make zero an invalid handle at the cost of decreasing the maximum ++ * number of blocks (2^28) by 1. Alternatively, a spare bit could be ++ * used to indicated valid blocks but there are likely to be better ++ * uses for spare bits. e.g. allowing more subpools ++ */ ++#define INDEX_OFFSET 1 ++ ++#define VCOS_BLOCKPOOL_HANDLE_GET_INDEX(h) \ ++ (((h) >> VCOS_BLOCKPOOL_SUBPOOL_BITS) - INDEX_OFFSET) ++ ++#define VCOS_BLOCKPOOL_HANDLE_GET_SUBPOOL(h) \ ++ ((h) & ((1 << VCOS_BLOCKPOOL_SUBPOOL_BITS) - 1)) ++ ++#define VCOS_BLOCKPOOL_HANDLE_CREATE(i,s) \ ++ ((((i) + INDEX_OFFSET) << VCOS_BLOCKPOOL_SUBPOOL_BITS) | (s)) ++ ++#define VCOS_BLOCKPOOL_INVALID_HANDLE 0 ++ ++typedef struct VCOS_BLOCKPOOL_HEADER_TAG ++{ ++ /* Blocks either refer to to the pool if they are allocated ++ * or the free list if they are available. ++ */ ++ union { ++ struct VCOS_BLOCKPOOL_HEADER_TAG *next; ++ struct VCOS_BLOCKPOOL_SUBPOOL_TAG* subpool; ++ } owner; ++} VCOS_BLOCKPOOL_HEADER_T; ++ ++typedef struct VCOS_BLOCKPOOL_SUBPOOL_TAG ++{ ++ /** VCOS_BLOCKPOOL_SUBPOOL_MAGIC */ ++ uint32_t magic; ++ VCOS_BLOCKPOOL_HEADER_T* free_list; ++ /* The start of the pool memory */ ++ void *mem; ++ /* Address of the first block header */ ++ void *start; ++ /** The number of blocks in this sub-pool */ ++ VCOS_UNSIGNED num_blocks; ++ /** Current number of available blocks in this sub-pool */ ++ VCOS_UNSIGNED available_blocks; ++ /** Pointers to the pool that owns this sub-pool */ ++ struct VCOS_BLOCKPOOL_TAG* owner; ++ /** Define properties such as memory ownership */ ++ uint32_t flags; ++} VCOS_BLOCKPOOL_SUBPOOL_T; ++ ++typedef struct VCOS_BLOCKPOOL_TAG ++{ ++ /** VCOS_BLOCKPOOL_MAGIC */ ++ uint32_t magic; ++ /** Thread safety for Alloc, Free, Delete, Stats */ ++ VCOS_MUTEX_T mutex; ++ /** The size of the block data */ ++ size_t block_data_size; ++ /** Block size inc overheads */ ++ size_t block_size; ++ /** Name for debugging */ ++ const char *name; ++ /* The number of subpools that may be used */ ++ VCOS_UNSIGNED num_subpools; ++ /** Number of blocks in each dynamically allocated subpool */ ++ VCOS_UNSIGNED num_extension_blocks; ++ /** Array of subpools. Subpool zero is is not deleted until the pool is ++ * destroed. If the index of the pool is < num_subpools and ++ * subpool[index.mem] is null then the subpool entry is valid but ++ * "not currently allocated" */ ++ VCOS_BLOCKPOOL_SUBPOOL_T subpools[VCOS_BLOCKPOOL_MAX_SUBPOOLS]; ++} VCOS_BLOCKPOOL_T; ++ ++#define VCOS_BLOCKPOOL_ROUND_UP(x,s) (((x) + ((s) - 1)) & ~((s) - 1)) ++/** ++ * Calculates the size in bytes required for a block pool containing ++ * num_blocks of size block_size plus any overheads. ++ * ++ * The block pool header (VCOS_BLOCKPOOL_T) is allocated separately ++ * ++ * Overheads: ++ * block_size + header must be a multiple of sizeof(void*) ++ * The start of the first block may need to be up to wordsize - 1 bytes ++ * into the given buffer because statically allocated buffers within structures ++ * are not guaranteed to be word aligned. ++ */ ++#define VCOS_BLOCKPOOL_SIZE(num_blocks, block_size) \ ++ ((VCOS_BLOCKPOOL_ROUND_UP((block_size) + sizeof(VCOS_BLOCKPOOL_HEADER_T), \ ++ sizeof(void*)) * (num_blocks)) + sizeof(void*)) ++ ++/** ++ * Sanity check to verify whether a handle is potentially a blockpool handle ++ * when the pool pointer is not available. ++ * ++ * If the pool pointer is availabe use vcos_blockpool_elem_to_handle instead. ++ * ++ * @param handle the handle to verify ++ * @param max_blocks the expected maximum number of block in the pool ++ * that the handle belongs to. ++ */ ++#define VCOS_BLOCKPOOL_IS_VALID_HANDLE_FORMAT(handle, max_blocks) \ ++ ((handle) != VCOS_BLOCKPOOL_INVALID_HANDLE \ ++ && VCOS_BLOCKPOOL_HANDLE_GET_INDEX((handle)) < (max_blocks)) ++ ++VCOSPRE_ ++ VCOS_STATUS_T VCOSPOST_ vcos_generic_blockpool_init(VCOS_BLOCKPOOL_T *pool, ++ VCOS_UNSIGNED num_blocks, VCOS_UNSIGNED block_size, ++ void *start, VCOS_UNSIGNED pool_size, const char *name); ++ ++VCOSPRE_ ++ VCOS_STATUS_T VCOSPOST_ vcos_generic_blockpool_create_on_heap( ++ VCOS_BLOCKPOOL_T *pool, VCOS_UNSIGNED num_blocks, ++ VCOS_UNSIGNED block_size, const char *name); ++ ++VCOSPRE_ ++ VCOS_STATUS_T VCOSPOST_ vcos_generic_blockpool_extend(VCOS_BLOCKPOOL_T *pool, ++ VCOS_UNSIGNED num_extensions, VCOS_UNSIGNED num_blocks); ++ ++VCOSPRE_ void VCOSPOST_ *vcos_generic_blockpool_alloc(VCOS_BLOCKPOOL_T *pool); ++ ++VCOSPRE_ void VCOSPOST_ *vcos_generic_blockpool_calloc(VCOS_BLOCKPOOL_T *pool); ++ ++VCOSPRE_ void VCOSPOST_ vcos_generic_blockpool_free(void *block); ++ ++VCOSPRE_ ++ VCOS_UNSIGNED VCOSPOST_ vcos_generic_blockpool_available_count( ++ VCOS_BLOCKPOOL_T *pool); ++ ++VCOSPRE_ ++ VCOS_UNSIGNED VCOSPOST_ vcos_generic_blockpool_used_count( ++ VCOS_BLOCKPOOL_T *pool); ++ ++VCOSPRE_ void VCOSPOST_ vcos_generic_blockpool_delete(VCOS_BLOCKPOOL_T *pool); ++ ++VCOSPRE_ uint32_t VCOSPOST_ vcos_generic_blockpool_elem_to_handle(void *block); ++ ++VCOSPRE_ void VCOSPOST_ ++ *vcos_generic_blockpool_elem_from_handle( ++ VCOS_BLOCKPOOL_T *pool, uint32_t handle); ++ ++VCOSPRE_ uint32_t VCOSPOST_ ++ vcos_generic_blockpool_is_valid_elem( ++ VCOS_BLOCKPOOL_T *pool, const void *block); ++#if defined(VCOS_INLINE_BODIES) ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_blockpool_init(VCOS_BLOCKPOOL_T *pool, ++ VCOS_UNSIGNED num_blocks, VCOS_UNSIGNED block_size, ++ void *start, VCOS_UNSIGNED pool_size, const char *name) ++{ ++ return vcos_generic_blockpool_init(pool, num_blocks, block_size, ++ start, pool_size, name); ++} ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_blockpool_create_on_heap(VCOS_BLOCKPOOL_T *pool, ++ VCOS_UNSIGNED num_blocks, VCOS_UNSIGNED block_size, const char *name) ++{ ++ return vcos_generic_blockpool_create_on_heap( ++ pool, num_blocks, block_size, name); ++} ++ ++VCOS_INLINE_IMPL ++ VCOS_STATUS_T VCOSPOST_ vcos_blockpool_extend(VCOS_BLOCKPOOL_T *pool, ++ VCOS_UNSIGNED num_extensions, VCOS_UNSIGNED num_blocks) ++{ ++ return vcos_generic_blockpool_extend(pool, num_extensions, num_blocks); ++} ++ ++VCOS_INLINE_IMPL ++void *vcos_blockpool_alloc(VCOS_BLOCKPOOL_T *pool) ++{ ++ return vcos_generic_blockpool_alloc(pool); ++} ++ ++VCOS_INLINE_IMPL ++void *vcos_blockpool_calloc(VCOS_BLOCKPOOL_T *pool) ++{ ++ return vcos_generic_blockpool_calloc(pool); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_blockpool_free(void *block) ++{ ++ vcos_generic_blockpool_free(block); ++} ++ ++VCOS_INLINE_IMPL ++VCOS_UNSIGNED vcos_blockpool_available_count(VCOS_BLOCKPOOL_T *pool) ++{ ++ return vcos_generic_blockpool_available_count(pool); ++} ++ ++VCOS_INLINE_IMPL ++VCOS_UNSIGNED vcos_blockpool_used_count(VCOS_BLOCKPOOL_T *pool) ++{ ++ return vcos_generic_blockpool_used_count(pool); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_blockpool_delete(VCOS_BLOCKPOOL_T *pool) ++{ ++ vcos_generic_blockpool_delete(pool); ++} ++ ++VCOS_INLINE_IMPL ++uint32_t vcos_blockpool_elem_to_handle(void *block) ++{ ++ return vcos_generic_blockpool_elem_to_handle(block); ++} ++ ++VCOS_INLINE_IMPL ++void *vcos_blockpool_elem_from_handle(VCOS_BLOCKPOOL_T *pool, uint32_t handle) ++{ ++ return vcos_generic_blockpool_elem_from_handle(pool, handle); ++} ++ ++VCOS_INLINE_IMPL ++uint32_t vcos_blockpool_is_valid_elem(VCOS_BLOCKPOOL_T *pool, const void *block) ++{ ++ return vcos_generic_blockpool_is_valid_elem(pool, block); ++} ++#endif /* VCOS_INLINE_BODIES */ ++ ++ ++#ifdef __cplusplus ++} ++#endif ++#endif /* VCOS_GENERIC_BLOCKPOOL_H */ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.c +@@ -0,0 +1,297 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - event flags implemented via mutexes ++=============================================================================*/ ++ ++#include "interface/vcos/vcos.h" ++#include "interface/vcos/generic/vcos_generic_event_flags.h" ++ ++#include <stddef.h> ++ ++/** A structure created by a thread that waits on the event flags ++ * for a particular combination of flags to arrive. ++ */ ++typedef struct VCOS_EVENT_WAITER_T ++{ ++ VCOS_UNSIGNED requested_events; /**< The events wanted */ ++ VCOS_UNSIGNED actual_events; /**< Actual events found */ ++ VCOS_UNSIGNED op; /**< The event operation to be used */ ++ VCOS_STATUS_T return_status; /**< The return status the waiter should pass back */ ++ VCOS_EVENT_FLAGS_T *flags; /**< Pointer to the original 'flags' structure */ ++ VCOS_THREAD_T *thread; /**< Thread waiting */ ++ struct VCOS_EVENT_WAITER_T *next; ++} VCOS_EVENT_WAITER_T; ++ ++#ifndef NDEBUG ++static int waiter_list_valid(VCOS_EVENT_FLAGS_T *flags); ++#endif ++static void event_flags_timer_expired(void *cxt); ++ ++VCOS_STATUS_T vcos_generic_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name) ++{ ++ VCOS_STATUS_T rc; ++ if ((rc=vcos_mutex_create(&flags->lock, name)) != VCOS_SUCCESS) ++ { ++ return rc; ++ } ++ ++ flags->events = 0; ++ flags->waiters.head = flags->waiters.tail = 0; ++ return rc; ++} ++ ++void vcos_generic_event_flags_set(VCOS_EVENT_FLAGS_T *flags, ++ VCOS_UNSIGNED bitmask, ++ VCOS_OPTION op) ++{ ++ vcos_assert(flags); ++ vcos_mutex_lock(&flags->lock); ++ if (op == VCOS_OR) ++ { ++ flags->events |= bitmask; ++ } ++ else if (op == VCOS_AND) ++ { ++ flags->events &= bitmask; ++ } ++ else ++ { ++ vcos_assert(0); ++ } ++ ++ /* Now wake up any threads that have now become signalled. */ ++ if (flags->waiters.head != NULL) ++ { ++ VCOS_UNSIGNED consumed_events = 0; ++ VCOS_EVENT_WAITER_T **pcurrent_waiter = &flags->waiters.head; ++ VCOS_EVENT_WAITER_T *prev_waiter = NULL; ++ ++ /* Walk the chain of tasks suspend on this event flag group to determine ++ * if any of their requests can be satisfied. ++ */ ++ while ((*pcurrent_waiter) != NULL) ++ { ++ VCOS_EVENT_WAITER_T *curr_waiter = *pcurrent_waiter; ++ ++ /* Determine if this request has been satisfied */ ++ ++ /* First, find the event flags in common. */ ++ VCOS_UNSIGNED waiter_satisfied = flags->events & curr_waiter->requested_events; ++ ++ /* Second, determine if all the event flags must match */ ++ if (curr_waiter->op & VCOS_AND) ++ { ++ /* All requested events must be present */ ++ waiter_satisfied = (waiter_satisfied == curr_waiter->requested_events); ++ } ++ ++ /* Wake this one up? */ ++ if (waiter_satisfied) ++ { ++ ++ if (curr_waiter->op & VCOS_CONSUME) ++ { ++ consumed_events |= curr_waiter->requested_events; ++ } ++ ++ /* remove this block from the list, taking care at the end */ ++ *pcurrent_waiter = curr_waiter->next; ++ if (curr_waiter->next == NULL) ++ flags->waiters.tail = prev_waiter; ++ ++ vcos_assert(waiter_list_valid(flags)); ++ ++ curr_waiter->return_status = VCOS_SUCCESS; ++ curr_waiter->actual_events = flags->events; ++ ++ _vcos_thread_sem_post(curr_waiter->thread); ++ } ++ else ++ { ++ /* move to next element in the list */ ++ prev_waiter = *pcurrent_waiter; ++ pcurrent_waiter = &(curr_waiter->next); ++ } ++ } ++ ++ flags->events &= ~consumed_events; ++ ++ } ++ ++ vcos_mutex_unlock(&flags->lock); ++} ++ ++void vcos_generic_event_flags_delete(VCOS_EVENT_FLAGS_T *flags) ++{ ++ vcos_mutex_delete(&flags->lock); ++} ++ ++extern VCOS_STATUS_T vcos_generic_event_flags_get(VCOS_EVENT_FLAGS_T *flags, ++ VCOS_UNSIGNED bitmask, ++ VCOS_OPTION op, ++ VCOS_UNSIGNED suspend, ++ VCOS_UNSIGNED *retrieved_bits) ++{ ++ VCOS_EVENT_WAITER_T waitreq; ++ VCOS_STATUS_T rc = VCOS_EAGAIN; ++ int satisfied = 0; ++ ++ vcos_assert(flags); ++ ++ /* default retrieved bits to 0 */ ++ *retrieved_bits = 0; ++ ++ vcos_mutex_lock(&flags->lock); ++ switch (op & VCOS_EVENT_FLAG_OP_MASK) ++ { ++ case VCOS_AND: ++ if ((flags->events & bitmask) == bitmask) ++ { ++ *retrieved_bits = flags->events; ++ rc = VCOS_SUCCESS; ++ satisfied = 1; ++ if (op & VCOS_CONSUME) ++ flags->events &= ~bitmask; ++ } ++ break; ++ ++ case VCOS_OR: ++ if (flags->events & bitmask) ++ { ++ *retrieved_bits = flags->events; ++ rc = VCOS_SUCCESS; ++ satisfied = 1; ++ if (op & VCOS_CONSUME) ++ flags->events &= ~bitmask; ++ } ++ break; ++ ++ default: ++ vcos_assert(0); ++ rc = VCOS_EINVAL; ++ break; ++ } ++ ++ if (!satisfied && suspend) ++ { ++ /* Have to go to sleep. ++ * ++ * Append to tail so we get FIFO ordering. ++ */ ++ waitreq.requested_events = bitmask; ++ waitreq.op = op; ++ waitreq.return_status = VCOS_EAGAIN; ++ waitreq.flags = flags; ++ waitreq.actual_events = 0; ++ waitreq.thread = vcos_thread_current(); ++ waitreq.next = 0; ++ vcos_assert(waitreq.thread != (VCOS_THREAD_T*)-1); ++ VCOS_QUEUE_APPEND_TAIL(&flags->waiters, &waitreq); ++ ++ if (suspend != (VCOS_UNSIGNED)-1) ++ _vcos_task_timer_set(event_flags_timer_expired, &waitreq, suspend); ++ ++ vcos_mutex_unlock(&flags->lock); ++ /* go to sleep and wait to be signalled or timeout */ ++ ++ _vcos_thread_sem_wait(); ++ ++ *retrieved_bits = waitreq.actual_events; ++ rc = waitreq.return_status; ++ ++ /* cancel the timer - do not do this while holding the mutex as it ++ * might be waiting for the timeout function to complete, which will ++ * try to take the mutex. ++ */ ++ if (suspend != (VCOS_UNSIGNED)-1) ++ _vcos_task_timer_cancel(); ++ } ++ else ++ { ++ vcos_mutex_unlock(&flags->lock); ++ } ++ ++ return rc; ++} ++ ++ ++/** Called when a get call times out. Remove this thread's ++ * entry from the waiting queue, then resume the thread. ++ */ ++static void event_flags_timer_expired(void *cxt) ++{ ++ VCOS_EVENT_WAITER_T *waitreq = (VCOS_EVENT_WAITER_T *)cxt; ++ VCOS_EVENT_FLAGS_T *flags = waitreq->flags; ++ VCOS_EVENT_WAITER_T **plist; ++ VCOS_EVENT_WAITER_T *prev = NULL; ++ VCOS_THREAD_T *thread = 0; ++ ++ vcos_assert(flags); ++ ++ vcos_mutex_lock(&flags->lock); ++ ++ /* walk the list of waiting threads on this event group, and remove ++ * the one that has expired. ++ * ++ * FIXME: could use doubly-linked list if lots of threads are found ++ * to be waiting on a single event flag instance. ++ */ ++ plist = &flags->waiters.head; ++ while (*plist != NULL) ++ { ++ if (*plist == waitreq) ++ { ++ int at_end; ++ /* found it */ ++ thread = (*plist)->thread; ++ at_end = ((*plist)->next == NULL); ++ ++ /* link past */ ++ *plist = (*plist)->next; ++ if (at_end) ++ flags->waiters.tail = prev; ++ ++ break; ++ } ++ prev = *plist; ++ plist = &(*plist)->next; ++ } ++ vcos_assert(waiter_list_valid(flags)); ++ ++ vcos_mutex_unlock(&flags->lock); ++ ++ if (thread) ++ { ++ _vcos_thread_sem_post(thread); ++ } ++} ++ ++#ifndef NDEBUG ++ ++static int waiter_list_valid(VCOS_EVENT_FLAGS_T *flags) ++{ ++ int valid; ++ /* Either both head and tail are NULL, or neither are NULL */ ++ if (flags->waiters.head == NULL) ++ { ++ valid = (flags->waiters.tail == NULL); ++ } ++ else ++ { ++ valid = (flags->waiters.tail != NULL); ++ } ++ ++ /* If head and tail point at the same non-NULL element, then there ++ * is only one element in the list. ++ */ ++ if (flags->waiters.head && (flags->waiters.head == flags->waiters.tail)) ++ { ++ valid = (flags->waiters.head->next == NULL); ++ } ++ return valid; ++} ++ ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.h +@@ -0,0 +1,104 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - event flags implemented via a semaphore ++=============================================================================*/ ++ ++#ifndef VCOS_GENERIC_EVENT_FLAGS_H ++#define VCOS_GENERIC_EVENT_FLAGS_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++ ++/** ++ * \file ++ * ++ * This provides event flags (as per Nucleus Event Groups) based on a ++ * mutex, a semaphore (per waiting thread) and a timer (per waiting ++ * thread). ++ * ++ * The data structure is a 32 bit unsigned int (the current set of ++ * flags) and a linked list of clients waiting to be 'satisfied'. ++ * ++ * The mutex merely locks access to the data structure. If a client ++ * calls vcos_event_flags_get() and the requested bits are not already ++ * present, it then sleeps on its per-thread semaphore after adding ++ * this semaphore to the queue waiting. It also sets up a timer. ++ * ++ * The per-thread semaphore and timer are actually stored in the ++ * thread context (joinable thread). In future it may become necessary ++ * to support non-VCOS threads by using thread local storage to ++ * create these objects and associate them with the thread. ++ */ ++ ++struct VCOS_EVENT_WAITER_T; ++ ++typedef struct VCOS_EVENT_FLAGS_T ++{ ++ VCOS_UNSIGNED events; /**< Events currently set */ ++ VCOS_MUTEX_T lock; /**< Serialize access */ ++ struct ++ { ++ struct VCOS_EVENT_WAITER_T *head; /**< List of threads waiting */ ++ struct VCOS_EVENT_WAITER_T *tail; /**< List of threads waiting */ ++ } waiters; ++} VCOS_EVENT_FLAGS_T; ++ ++#define VCOS_OR 1 ++#define VCOS_AND 2 ++#define VCOS_CONSUME 4 ++#define VCOS_OR_CONSUME (VCOS_OR | VCOS_CONSUME) ++#define VCOS_AND_CONSUME (VCOS_AND | VCOS_CONSUME) ++#define VCOS_EVENT_FLAG_OP_MASK (VCOS_OR|VCOS_AND) ++ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_generic_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name); ++VCOSPRE_ void VCOSPOST_ vcos_generic_event_flags_set(VCOS_EVENT_FLAGS_T *flags, ++ VCOS_UNSIGNED events, ++ VCOS_OPTION op); ++VCOSPRE_ void VCOSPOST_ vcos_generic_event_flags_delete(VCOS_EVENT_FLAGS_T *); ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_generic_event_flags_get(VCOS_EVENT_FLAGS_T *flags, ++ VCOS_UNSIGNED requested_events, ++ VCOS_OPTION op, ++ VCOS_UNSIGNED suspend, ++ VCOS_UNSIGNED *retrieved_events); ++ ++#ifdef VCOS_INLINE_BODIES ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name) { ++ return vcos_generic_event_flags_create(flags, name); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_event_flags_set(VCOS_EVENT_FLAGS_T *flags, ++ VCOS_UNSIGNED events, ++ VCOS_OPTION op) { ++ vcos_generic_event_flags_set(flags, events, op); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_event_flags_delete(VCOS_EVENT_FLAGS_T *f) { ++ vcos_generic_event_flags_delete(f); ++} ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_event_flags_get(VCOS_EVENT_FLAGS_T *flags, ++ VCOS_UNSIGNED requested_events, ++ VCOS_OPTION op, ++ VCOS_UNSIGNED suspend, ++ VCOS_UNSIGNED *retrieved_events) { ++ return vcos_generic_event_flags_get(flags, requested_events, op, suspend, retrieved_events); ++} ++ ++#endif /* VCOS_INLINE_BODIES */ ++ ++#ifdef __cplusplus ++} ++#endif ++#endif ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_named_sem.h +@@ -0,0 +1,81 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - named semaphores ++=============================================================================*/ ++ ++#ifndef VCOS_GENERIC_NAMED_SEM_H ++#define VCOS_GENERIC_NAMED_SEM_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++ ++/** ++ * \file ++ * ++ * Generic support for named semaphores, using regular ones. This is only ++ * suitable for emulating them on an embedded MMUless system, since there is ++ * no support for opening semaphores across process boundaries. ++ * ++ */ ++ ++#define VCOS_NAMED_SEMAPHORE_NAMELEN 64 ++ ++/* In theory we could use the name facility provided within Nucleus. However, this ++ * is hard to do as semaphores are constantly being created and destroyed; we ++ * would need to stop everything while allocating the memory for the semaphore ++ * list and then walking it. So keep our own list. ++ */ ++typedef struct VCOS_NAMED_SEMAPHORE_T ++{ ++ struct VCOS_NAMED_SEMAPHORE_IMPL_T *actual; /**< There are 'n' named semaphores per 1 actual semaphore */ ++ VCOS_SEMAPHORE_T *sem; /**< Pointer to actual underlying semaphore */ ++} VCOS_NAMED_SEMAPHORE_T; ++ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ ++vcos_generic_named_semaphore_create(VCOS_NAMED_SEMAPHORE_T *sem, const char *name, VCOS_UNSIGNED count); ++ ++VCOSPRE_ void VCOSPOST_ vcos_named_semaphore_delete(VCOS_NAMED_SEMAPHORE_T *sem); ++ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ _vcos_named_semaphore_init(void); ++VCOSPRE_ void VCOSPOST_ _vcos_named_semaphore_deinit(void); ++ ++#if defined(VCOS_INLINE_BODIES) ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_named_semaphore_create(VCOS_NAMED_SEMAPHORE_T *sem, const char *name, VCOS_UNSIGNED count) { ++ return vcos_generic_named_semaphore_create(sem, name, count); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_named_semaphore_wait(VCOS_NAMED_SEMAPHORE_T *sem) { ++ vcos_semaphore_wait(sem->sem); ++} ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_named_semaphore_trywait(VCOS_NAMED_SEMAPHORE_T *sem) { ++ return vcos_semaphore_trywait(sem->sem); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_named_semaphore_post(VCOS_NAMED_SEMAPHORE_T *sem) { ++ vcos_semaphore_post(sem->sem); ++} ++ ++ ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++#endif ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_quickslow_mutex.h +@@ -0,0 +1,75 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - reentrant mutexes created from regular ones. ++=============================================================================*/ ++ ++#ifndef VCOS_GENERIC_QUICKSLOW_MUTEX_H ++#define VCOS_GENERIC_QUICKSLOW_MUTEX_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++ ++/** ++ * \file ++ * ++ * Quickslow Mutexes implemented as regular ones (i.e. quick and slow modes are the same). ++ * ++ */ ++ ++typedef VCOS_MUTEX_T VCOS_QUICKSLOW_MUTEX_T; ++ ++#if defined(VCOS_INLINE_BODIES) ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_quickslow_mutex_create(VCOS_QUICKSLOW_MUTEX_T *m, const char *name) ++{ ++ return vcos_mutex_create(m, name); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_quickslow_mutex_delete(VCOS_QUICKSLOW_MUTEX_T *m) ++{ ++ vcos_mutex_delete(m); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_quickslow_mutex_lock(VCOS_QUICKSLOW_MUTEX_T *m) ++{ ++ while (vcos_mutex_lock(m) == VCOS_EAGAIN); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_quickslow_mutex_unlock(VCOS_QUICKSLOW_MUTEX_T *m) ++{ ++ vcos_mutex_unlock(m); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_quickslow_mutex_lock_quick(VCOS_QUICKSLOW_MUTEX_T *m) ++{ ++ while (vcos_mutex_lock(m) == VCOS_EAGAIN); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_quickslow_mutex_unlock_quick(VCOS_QUICKSLOW_MUTEX_T *m) ++{ ++ vcos_mutex_unlock(m); ++} ++ ++#endif ++ ++ ++#ifdef __cplusplus ++} ++#endif ++#endif ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_reentrant_mtx.h +@@ -0,0 +1,75 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - reentrant mutexes created from regular ones. ++=============================================================================*/ ++ ++#ifndef VCOS_GENERIC_REENTRANT_MUTEX_H ++#define VCOS_GENERIC_REENTRANT_MUTEX_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++ ++/** ++ * \file ++ * ++ * Reentrant Mutexes from regular ones. ++ * ++ */ ++ ++typedef struct VCOS_REENTRANT_MUTEX_T ++{ ++ VCOS_MUTEX_T mutex; ++ VCOS_THREAD_T *owner; ++ unsigned count; ++} VCOS_REENTRANT_MUTEX_T; ++ ++/* Extern definitions of functions that do the actual work */ ++ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_generic_reentrant_mutex_create(VCOS_REENTRANT_MUTEX_T *m, const char *name); ++ ++VCOSPRE_ void VCOSPOST_ vcos_generic_reentrant_mutex_delete(VCOS_REENTRANT_MUTEX_T *m); ++ ++VCOSPRE_ void VCOSPOST_ vcos_generic_reentrant_mutex_lock(VCOS_REENTRANT_MUTEX_T *m); ++ ++VCOSPRE_ void VCOSPOST_ vcos_generic_reentrant_mutex_unlock(VCOS_REENTRANT_MUTEX_T *m); ++ ++/* Inline forwarding functions */ ++ ++#if defined(VCOS_INLINE_BODIES) ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_reentrant_mutex_create(VCOS_REENTRANT_MUTEX_T *m, const char *name) { ++ return vcos_generic_reentrant_mutex_create(m,name); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_reentrant_mutex_delete(VCOS_REENTRANT_MUTEX_T *m) { ++ vcos_generic_reentrant_mutex_delete(m); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_reentrant_mutex_lock(VCOS_REENTRANT_MUTEX_T *m) { ++ vcos_generic_reentrant_mutex_lock(m); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_reentrant_mutex_unlock(VCOS_REENTRANT_MUTEX_T *m) { ++ vcos_generic_reentrant_mutex_unlock(m); ++} ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++#endif ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_tls.h +@@ -0,0 +1,144 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - generic thread local storage ++=============================================================================*/ ++ ++#ifndef VCOS_GENERIC_TLS_H ++#define VCOS_GENERIC_TLS_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++ ++/** ++ * \file ++ * ++ * Do an emulation of Thread Local Storage. The platform needs to ++ * provide a way to set and get a per-thread pointer which is ++ * where the TLS data itself is stored. ++ * ++ * ++ * Each thread that wants to join in this scheme needs to call ++ * vcos_tls_thread_register(). ++ * ++ * The platform needs to support the macros/functions ++ * _vcos_tls_thread_ptr_set() and _vcos_tls_thread_ptr_get(). ++ */ ++ ++#ifndef VCOS_WANT_TLS_EMULATION ++#error Should not be included unless TLS emulation is defined ++#endif ++ ++/** Number of slots to reserve per thread. This results in an overhead ++ * of this many words per thread. ++ */ ++#define VCOS_TLS_MAX_SLOTS 4 ++ ++/** TLS key. Allocating one of these reserves the client one of the ++ * available slots. ++ */ ++typedef VCOS_UNSIGNED VCOS_TLS_KEY_T; ++ ++/** TLS per-thread structure. Each thread gets one of these ++ * if TLS emulation (rather than native TLS support) is ++ * being used. ++ */ ++typedef struct VCOS_TLS_THREAD_T ++{ ++ void *slots[VCOS_TLS_MAX_SLOTS]; ++} VCOS_TLS_THREAD_T; ++ ++/* ++ * Internal APIs ++ */ ++ ++/** Register this thread's TLS storage area. */ ++VCOSPRE_ void VCOSPOST_ vcos_tls_thread_register(VCOS_TLS_THREAD_T *); ++ ++/** Create a new TLS key */ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_generic_tls_create(VCOS_TLS_KEY_T *key); ++ ++/** Delete a TLS key */ ++VCOSPRE_ void VCOSPOST_ vcos_generic_tls_delete(VCOS_TLS_KEY_T tls); ++ ++/** Initialise the TLS library */ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_tls_init(void); ++ ++/** Deinitialise the TLS library */ ++VCOSPRE_ void VCOSPOST_ vcos_tls_deinit(void); ++ ++#if defined(VCOS_INLINE_BODIES) ++ ++#undef VCOS_ASSERT_LOGGING_DISABLE ++#define VCOS_ASSERT_LOGGING_DISABLE 1 ++ ++/* ++ * Implementations of public API functions ++ */ ++ ++/** Set the given value. Since everything is per-thread, there is no need ++ * for any locking. ++ */ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_tls_set(VCOS_TLS_KEY_T tls, void *v) { ++ VCOS_TLS_THREAD_T *tlsdata = _vcos_tls_thread_ptr_get(); ++ vcos_assert(tlsdata); /* Fires if this thread has not been registered */ ++ if (tls<VCOS_TLS_MAX_SLOTS) ++ { ++ tlsdata->slots[tls] = v; ++ return VCOS_SUCCESS; ++ } ++ else ++ { ++ vcos_assert(0); ++ return VCOS_EINVAL; ++ } ++} ++ ++/** Get the given value. No locking required. ++ */ ++VCOS_INLINE_IMPL ++void *vcos_tls_get(VCOS_TLS_KEY_T tls) { ++ VCOS_TLS_THREAD_T *tlsdata = _vcos_tls_thread_ptr_get(); ++ vcos_assert(tlsdata); /* Fires if this thread has not been registered */ ++ if (tls<VCOS_TLS_MAX_SLOTS) ++ { ++ return tlsdata->slots[tls]; ++ } ++ else ++ { ++ vcos_assert(0); ++ return NULL; ++ } ++} ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_tls_create(VCOS_TLS_KEY_T *key) { ++ return vcos_generic_tls_create(key); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_tls_delete(VCOS_TLS_KEY_T tls) { ++ vcos_generic_tls_delete(tls); ++} ++ ++#undef VCOS_ASSERT_LOGGING_DISABLE ++#define VCOS_ASSERT_LOGGING_DISABLE 0 ++ ++#endif /* VCOS_INLINE_BODIES */ ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_joinable_thread_from_plain.h +@@ -0,0 +1,202 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Module : vcos ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - implementation: joinable thread from plain ++=============================================================================*/ ++ ++/** \file ++ * ++ * Header file for platforms creating the joinable thread from a lowlevel ++ * thread. ++ * ++ * In addition to the actual thread, the following are also created: ++ * ++ * - a semaphore to wait on when joining the thread ++ * - a semaphore to support counted suspend/resume (used by event group) ++ * - a per-thread timer (used by event group, but could be removed) ++ */ ++ ++#ifndef VCOS_JOINABLE_THREAD_FROM_PLAIN_H ++#define VCOS_JOINABLE_THREAD_FROM_PLAIN_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_semaphore.h" ++#include "interface/vcos/vcos_lowlevel_thread.h" ++#include "interface/vcos/vcos_timer.h" ++ ++#ifdef VCOS_WANT_TLS_EMULATION ++#include "interface/vcos/generic/vcos_generic_tls.h" ++#endif ++ ++#define VCOS_THREAD_MAGIC 0x56436a74 ++ ++#define VCOS_THREAD_VALID(t) (t->magic == VCOS_THREAD_MAGIC) ++#define VCOS_HAVE_THREAD_AT_EXIT 1 ++ ++/** Thread attribute structure. Clients should not manipulate this directly, but ++ * should instead use the provided functions. ++ */ ++typedef struct VCOS_THREAD_ATTR_T ++{ ++ void *ta_stackaddr; ++ VCOS_UNSIGNED ta_stacksz; ++ VCOS_UNSIGNED ta_priority; ++ VCOS_UNSIGNED ta_affinity; ++ VCOS_UNSIGNED ta_timeslice; ++ VCOS_UNSIGNED legacy; ++ VCOS_UNSIGNED ta_autostart; ++} VCOS_THREAD_ATTR_T; ++ ++/** Each thread gets a timer, which is for internal VCOS use. ++ */ ++typedef struct _VCOS_THREAD_TIMER_T ++{ ++ VCOS_TIMER_T timer; ++ void (*pfn)(void *); ++ void *cxt; ++} _VCOS_THREAD_TIMER_T; ++ ++typedef void (*VCOS_THREAD_EXIT_HANDLER_T)(void *); ++/** Called at thread exit. ++ */ ++typedef struct VCOS_THREAD_EXIT_T ++{ ++ VCOS_THREAD_EXIT_HANDLER_T pfn; ++ void *cxt; ++} VCOS_THREAD_EXIT_T; ++#define VCOS_MAX_EXIT_HANDLERS 8 ++ ++/* The name field isn't used for anything, so we can just copy the ++ * the pointer. Nucleus makes its own copy. ++ */ ++typedef const char * VCOS_LLTHREAD_T_NAME; ++#define _VCOS_LLTHREAD_NAME(dst,src) (dst)=(src) ++ ++/* ++ * Simulated TLS support ++ */ ++ ++ ++/** Thread structure. ++ * ++ * \warning Do not access the members of this structure directly! ++ */ ++typedef struct VCOS_THREAD_T ++{ ++ VCOS_LLTHREAD_T thread; /**< The underlying thread */ ++ char name[16]; /**< The name */ ++ unsigned int magic; /**< For debug */ ++ void *exit_data; /**< Exit data passed out in vcos_joinable_thread_exit() */ ++ void *stack; /**< Stack, if not supplied by caller */ ++ VCOS_SEMAPHORE_T wait; /**< Semaphore to wait on at join */ ++ VCOS_SEMAPHORE_T suspend; /**< Semaphore to wait on for counted suspend */ ++ int16_t joined; /**< Joined yet? For debug. */ ++ VCOS_UNSIGNED legacy; /**< Use (argc,argv) for entry point arguments */ ++ void *(*entry)(void*); /**< Entry point */ ++ void *arg; /**< Argument passed to entry point */ ++ void *(*term)(void*); /**< Termination function, used by reaper */ ++ void *term_arg; /**< Argument passed to termination function */ ++ _VCOS_THREAD_TIMER_T _timer; /**< Internal timer, mainly for event groups */ ++#ifdef VCOS_WANT_TLS_EMULATION ++ VCOS_TLS_THREAD_T _tls; /**< TLS data when native TLS not available, or NULL */ ++#endif ++ /** Array of functions to call at thread exit */ ++ VCOS_THREAD_EXIT_T at_exit[VCOS_MAX_EXIT_HANDLERS]; ++ ++ struct VCOS_THREAD_T *next; /**< For linked lists of threads */ ++} VCOS_THREAD_T; ++ ++#if defined(VCOS_INLINE_BODIES) ++ ++VCOS_INLINE_IMPL ++void vcos_thread_attr_setstack(VCOS_THREAD_ATTR_T *attrs, void *addr, VCOS_UNSIGNED stacksz) { ++ attrs->ta_stackaddr = addr; ++ attrs->ta_stacksz = stacksz; ++} ++ ++VCOS_INLINE_IMPL ++void vcos_thread_attr_setstacksize(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED stacksz) { ++ attrs->ta_stacksz = stacksz; ++} ++ ++VCOS_INLINE_IMPL ++void vcos_thread_attr_setpriority(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED pri) { ++ attrs->ta_priority = pri; ++} ++ ++VCOS_INLINE_IMPL ++void vcos_thread_attr_setaffinity(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED affinity) { ++ attrs->ta_affinity = affinity; ++} ++ ++VCOS_INLINE_IMPL ++void vcos_thread_attr_settimeslice(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED ts) { ++ attrs->ta_timeslice = ts; ++} ++ ++VCOS_INLINE_IMPL ++void _vcos_thread_attr_setlegacyapi(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED legacy) { ++ attrs->legacy = legacy; ++} ++ ++VCOS_INLINE_IMPL ++void vcos_thread_attr_setautostart(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED autostart) { ++ attrs->ta_autostart = autostart; ++} ++ ++VCOS_INLINE_IMPL ++VCOS_THREAD_T *vcos_thread_current(void) { ++ VCOS_THREAD_T *ret = (VCOS_THREAD_T*)vcos_llthread_current(); ++ /*If we're called from a non-vcos thread, this assert will fail. ++ *XXX FIXME why is this commented out? ++ *vcos_assert(ret->magic == VCOS_THREAD_MAGIC); ++ */ ++ return ret; ++} ++ ++VCOS_INLINE_IMPL ++int vcos_thread_running(VCOS_THREAD_T *thread) { ++ return vcos_llthread_running(&thread->thread); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_thread_resume(VCOS_THREAD_T *thread) { ++ vcos_llthread_resume(&thread->thread); ++} ++ ++#endif /* VCOS_INLINE_BODIES */ ++ ++/** ++ * \brief Create a VCOS_THREAD_T for the current thread. This is so we can have ++ * VCOS_THREAD_Ts even for threads not originally created by VCOS (eg the ++ * thread that calls vcos_init) ++ */ ++extern VCOS_STATUS_T _vcos_thread_create_attach(VCOS_THREAD_T *thread, ++ const char *name); ++ ++/** ++ * \brief Deletes the VCOS_THREAD_T, but does not wait for the underlying ++ * thread to exit. This will cleanup everything created by ++ * _vcos_thread_create_attach ++ */ ++extern void _vcos_thread_delete(VCOS_THREAD_T *thread); ++ ++/** Register a function to be called when the current thread exits. ++ */ ++extern VCOS_STATUS_T vcos_thread_at_exit(void (*pfn)(void*), void *cxt); ++ ++/** Deregister a previously registered at-exit function. ++ */ ++extern void vcos_thread_deregister_at_exit(void (*pfn)(void*), void *cxt); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif /* VCOS_JOINABLE_THREAD_FROM_PLAIN_H */ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_latch_from_sem.h +@@ -0,0 +1,48 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : vcos ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - Construct a latch from a semaphore ++=============================================================================*/ ++ ++/** FIXME: rename to vcos_mutex_from_sem.c ++ */ ++ ++typedef struct VCOS_MUTEX_T { ++ VCOS_SEMAPHORE_T sem; ++ struct VCOS_THREAD_T *owner; ++} VCOS_MUTEX_T; ++ ++extern VCOS_STATUS_T vcos_generic_mutex_create(VCOS_MUTEX_T *latch, const char *name); ++extern void vcos_generic_mutex_delete(VCOS_MUTEX_T *latch); ++extern VCOS_STATUS_T vcos_generic_mutex_lock(VCOS_MUTEX_T *latch); ++extern void vcos_generic_mutex_unlock(VCOS_MUTEX_T *latch); ++ ++#if defined(VCOS_INLINE_BODIES) ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_mutex_create(VCOS_MUTEX_T *latch, const char *name) { ++ return vcos_generic_mutex_create(latch,name); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_mutex_delete(VCOS_MUTEX_T *latch) { ++ vcos_generic_mutex_delete(latch); ++} ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_mutex_lock(VCOS_MUTEX_T *latch) { ++ return vcos_generic_mutex_lock(latch); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_mutex_unlock(VCOS_MUTEX_T *latch) { ++ vcos_generic_mutex_unlock(latch); ++} ++ ++#endif /* VCOS_INLINE_BODIES */ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_logcat.c +@@ -0,0 +1,549 @@ ++/*============================================================================= ++Copyright (c) 2010 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : vcos ++ ++FILE DESCRIPTION ++Categorized logging for VCOS - a generic implementation. ++=============================================================================*/ ++ ++#include "interface/vcos/vcos.h" ++#include "interface/vcos/vcos_ctype.h" ++#include "interface/vcos/vcos_string.h" ++ ++static VCOS_MUTEX_T lock; ++static int warned_loglevel; /* only warn about invalid log level once */ ++static VCOS_VLOG_IMPL_FUNC_T vcos_vlog_impl_func = vcos_vlog_default_impl; ++ ++#define VCOS_LOG_CATEGORY (&dflt_log_category) ++static VCOS_LOG_CAT_T dflt_log_category; ++VCOS_LOG_CAT_T *vcos_logging_categories = NULL; ++static int inited; ++ ++#if VCOS_HAVE_CMD ++ ++/* ++ * For kernel or videocore purposes, we generally want the log command. For ++ * user-space apps, they might want to provide their own log command, so we ++ * don't include the built in on. ++ * ++ * So pthreads/vcos_platform.h defines VCOS_WANT_LOG_CMD to be 0. It is ++ * undefined elsewhere. ++ */ ++ ++# if !defined( VCOS_WANT_LOG_CMD ) ++# define VCOS_WANT_LOG_CMD 1 ++# endif ++#else ++# define VCOS_WANT_LOG_CMD 0 ++#endif ++ ++#if VCOS_WANT_LOG_CMD ++ ++/***************************************************************************** ++* ++* Does a vcos_assert(0), which is useful to test logging. ++* ++*****************************************************************************/ ++ ++VCOS_STATUS_T vcos_log_assert_cmd( VCOS_CMD_PARAM_T *param ) ++{ ++ (void)param; ++ ++#if defined( NDEBUG ) && !defined( VCOS_RELEASE_ASSERTS ) ++ vcos_log_error( "vcos_asserts have been compiled out" ); ++ vcos_cmd_printf( param, "vcos_asserts have been compiled out - did a vcos_log_error instead\n" ); ++#else ++ vcos_assert(0); ++ vcos_cmd_printf( param, "Executed vcos_assert(0)\n" ); ++#endif ++ ++ return VCOS_SUCCESS; ++} ++ ++/***************************************************************************** ++* ++* Sets a vcos logging level ++* ++*****************************************************************************/ ++ ++VCOS_STATUS_T vcos_log_set_cmd( VCOS_CMD_PARAM_T *param ) ++{ ++ VCOS_LOG_CAT_T *cat; ++ char *name; ++ char *levelStr; ++ VCOS_LOG_LEVEL_T level; ++ VCOS_STATUS_T status; ++ ++ if ( param->argc != 3 ) ++ { ++ vcos_cmd_usage( param ); ++ return VCOS_EINVAL; ++ } ++ ++ name = param->argv[1]; ++ levelStr = param->argv[2]; ++ ++ if ( vcos_string_to_log_level( levelStr, &level ) != VCOS_SUCCESS ) ++ { ++ vcos_cmd_printf( param, "Unrecognized logging level: '%s'\n", levelStr ); ++ return VCOS_EINVAL; ++ } ++ ++ vcos_mutex_lock(&lock); ++ ++ status = VCOS_SUCCESS; ++ for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next ) ++ { ++ if ( vcos_strcmp( name, cat->name ) == 0 ) ++ { ++ cat->level = level; ++ vcos_cmd_printf( param, "Category %s level set to %s\n", name, levelStr ); ++ break; ++ } ++ } ++ if ( cat == NULL ) ++ { ++ vcos_cmd_printf( param, "Unrecognized category: '%s'\n", name ); ++ status = VCOS_ENOENT; ++ } ++ ++ vcos_mutex_unlock(&lock); ++ ++ return status; ++} ++ ++/***************************************************************************** ++* ++* Prints out the current settings for a given category (or all cvategories) ++* ++*****************************************************************************/ ++ ++VCOS_STATUS_T vcos_log_status_cmd( VCOS_CMD_PARAM_T *param ) ++{ ++ VCOS_LOG_CAT_T *cat; ++ VCOS_STATUS_T status; ++ ++ vcos_mutex_lock(&lock); ++ ++ if ( param->argc == 1) ++ { ++ int nw; ++ int nameWidth = 0; ++ ++ /* Print information about all of the categories. */ ++ ++ for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next ) ++ { ++ nw = (int)strlen( cat->name ); ++ ++ if ( nw > nameWidth ) ++ { ++ nameWidth = nw; ++ } ++ } ++ ++ for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next ) ++ { ++ vcos_cmd_printf( param, "%-*s - %s\n", nameWidth, cat->name, vcos_log_level_to_string( cat->level )); ++ } ++ } ++ else ++ { ++ /* Print information about a particular category */ ++ ++ for ( cat = vcos_logging_categories; cat != NULL; cat = cat->next ) ++ { ++ if ( vcos_strcmp( cat->name, param->argv[1] ) == 0 ) ++ { ++ vcos_cmd_printf( param, "%s - %s\n", cat->name, vcos_log_level_to_string( cat->level )); ++ break; ++ } ++ } ++ if ( cat == NULL ) ++ { ++ vcos_cmd_printf( param, "Unrecognized logging category: '%s'\n", param->argv[1] ); ++ status = VCOS_ENOENT; ++ goto out; ++ } ++ } ++ ++ status = VCOS_SUCCESS; ++out: ++ vcos_mutex_unlock(&lock); ++ ++ return status; ++} ++ ++/***************************************************************************** ++* ++* Prints out the current settings for a given category (or all cvategories) ++* ++*****************************************************************************/ ++ ++VCOS_STATUS_T vcos_log_test_cmd( VCOS_CMD_PARAM_T *param ) ++{ ++ if ( param->argc == 1 ) ++ { ++ static int seq_num = 100; ++ ++ /* No additional arguments - generate a message with an incrementing number */ ++ ++ vcos_log_error( "Test message %d", seq_num ); ++ ++ seq_num++; ++ vcos_cmd_printf( param, "Logged 'Test message %d'\n", seq_num ); ++ } ++ else ++ { ++ int arg_idx; ++ ++ /* Arguments supplied - log these */ ++ ++ for ( arg_idx = 0; arg_idx < param->argc; arg_idx++ ) ++ { ++ vcos_log_error( "argv[%d] = '%s'", arg_idx, param->argv[arg_idx] ); ++ } ++ vcos_cmd_printf( param, "Logged %d line(s) of test data\n", param->argc ); ++ } ++ return VCOS_SUCCESS; ++} ++ ++/***************************************************************************** ++* ++* Internal commands ++* ++*****************************************************************************/ ++ ++static VCOS_CMD_T log_cmd_entry[] = ++{ ++ { "assert", "", vcos_log_assert_cmd, NULL, "Does a vcos_assert(0) to test logging" }, ++ { "set", "category level", vcos_log_set_cmd, NULL, "Sets the vcos logging level for a category" }, ++ { "status", "[category]", vcos_log_status_cmd, NULL, "Prints the vcos log status for a (or all) categories" }, ++ { "test", "[arbitrary text]", vcos_log_test_cmd, NULL, "Does a vcos_log to test logging" }, ++ ++ { NULL, NULL, NULL, NULL, NULL } ++}; ++ ++static VCOS_CMD_T cmd_log = ++ { "log", "command [args]", NULL, log_cmd_entry, "Commands related to vcos logging" }; ++ ++#endif ++ ++void vcos_logging_init(void) ++{ ++ if (inited) ++ { ++ /* FIXME: should print a warning or something here */ ++ return; ++ } ++ vcos_mutex_create(&lock, "vcos_log"); ++ ++ vcos_log_platform_init(); ++ ++ vcos_log_register("default", &dflt_log_category); ++ ++#if VCOS_WANT_LOG_CMD ++ vcos_cmd_register( &cmd_log ); ++#endif ++ ++ vcos_assert(!inited); ++ inited = 1; ++} ++ ++/** Read an alphanumeric token, returning True if we succeeded. ++ */ ++ ++static int read_tok(char *tok, size_t toklen, const char **pstr, char sep) ++{ ++ const char *str = *pstr; ++ size_t n = 0; ++ char ch; ++ ++ /* skip past any whitespace */ ++ while (str[0] && isspace((int)(str[0]))) ++ str++; ++ ++ while ((ch = *str) != '\0' && ++ ch != sep && ++ (isalnum((int)ch) || (ch == '_')) && ++ n != toklen-1) ++ { ++ tok[n++] = ch; ++ str++; ++ } ++ ++ /* did it work out? */ ++ if (ch == '\0' || ch == sep) ++ { ++ if (ch) str++; /* move to next token if not at end */ ++ /* yes */ ++ tok[n] = '\0'; ++ *pstr = str; ++ return 1; ++ } ++ else ++ { ++ /* no */ ++ return 0; ++ } ++} ++ ++const char *vcos_log_level_to_string( VCOS_LOG_LEVEL_T level ) ++{ ++ switch (level) ++ { ++ case VCOS_LOG_UNINITIALIZED: return "uninit"; ++ case VCOS_LOG_NEVER: return "never"; ++ case VCOS_LOG_ERROR: return "error"; ++ case VCOS_LOG_WARN: return "warn"; ++ case VCOS_LOG_INFO: return "info"; ++ case VCOS_LOG_TRACE: return "trace"; ++ } ++ return "???"; ++} ++ ++VCOS_STATUS_T vcos_string_to_log_level( const char *str, VCOS_LOG_LEVEL_T *level ) ++{ ++ if (strcmp(str,"error") == 0) ++ *level = VCOS_LOG_ERROR; ++ else if (strcmp(str,"never") == 0) ++ *level = VCOS_LOG_NEVER; ++ else if (strcmp(str,"warn") == 0) ++ *level = VCOS_LOG_WARN; ++ else if (strcmp(str,"warning") == 0) ++ *level = VCOS_LOG_WARN; ++ else if (strcmp(str,"info") == 0) ++ *level = VCOS_LOG_INFO; ++ else if (strcmp(str,"trace") == 0) ++ *level = VCOS_LOG_TRACE; ++ else ++ return VCOS_EINVAL; ++ ++ return VCOS_SUCCESS; ++} ++ ++static int read_level(VCOS_LOG_LEVEL_T *level, const char **pstr, char sep) ++{ ++ char buf[16]; ++ int ret = 1; ++ if (read_tok(buf,sizeof(buf),pstr,sep)) ++ { ++ if (vcos_string_to_log_level(buf,level) != VCOS_SUCCESS) ++ { ++ vcos_log("Invalid trace level '%s'\n", buf); ++ ret = 0; ++ } ++ } ++ else ++ { ++ ret = 0; ++ } ++ return ret; ++} ++ ++void vcos_log_register(const char *name, VCOS_LOG_CAT_T *category) ++{ ++ const char *env; ++ VCOS_LOG_CAT_T *i; ++ ++ category->name = name; ++ if ( category->level == VCOS_LOG_UNINITIALIZED ) ++ { ++ category->level = VCOS_LOG_ERROR; ++ } ++ category->flags.want_prefix = (category != &dflt_log_category ); ++ ++ vcos_mutex_lock(&lock); ++ ++ /* is it already registered? */ ++ for (i = vcos_logging_categories; i ; i = i->next ) ++ { ++ if (i == category) ++ { ++ i->refcount++; ++ break; ++ } ++ } ++ ++ if (!i) ++ { ++ /* not yet registered */ ++ category->next = vcos_logging_categories; ++ vcos_logging_categories = category; ++ category->refcount++; ++ ++ vcos_log_platform_register(category); ++ } ++ ++ vcos_mutex_unlock(&lock); ++ ++ /* Check to see if this log level has been enabled. Look for ++ * (<category:level>,)* ++ * ++ * VC_LOGLEVEL=ilcs:info,vchiq:warn ++ */ ++ ++ env = _VCOS_LOG_LEVEL(); ++ if (env) ++ { ++ do ++ { ++ char env_name[64]; ++ VCOS_LOG_LEVEL_T level; ++ if (read_tok(env_name, sizeof(env_name), &env, ':') && ++ read_level(&level, &env, ',')) ++ { ++ if (strcmp(env_name, name) == 0) ++ { ++ category->level = level; ++ break; ++ } ++ } ++ else ++ { ++ if (!warned_loglevel) ++ { ++ vcos_log("VC_LOGLEVEL format invalid at %s\n", env); ++ warned_loglevel = 1; ++ } ++ return; ++ } ++ } while (env[0] != '\0'); ++ } ++ ++ vcos_log_info( "Registered log category '%s' with level %s", ++ category->name, ++ vcos_log_level_to_string( category->level )); ++} ++ ++void vcos_log_unregister(VCOS_LOG_CAT_T *category) ++{ ++ VCOS_LOG_CAT_T **pcat; ++ vcos_mutex_lock(&lock); ++ category->refcount--; ++ if (category->refcount == 0) ++ { ++ pcat = &vcos_logging_categories; ++ while (*pcat != category) ++ { ++ if (!*pcat) ++ break; /* possibly deregistered twice? */ ++ if ((*pcat)->next == NULL) ++ { ++ vcos_assert(0); /* already removed! */ ++ vcos_mutex_unlock(&lock); ++ return; ++ } ++ pcat = &(*pcat)->next; ++ } ++ if (*pcat) ++ *pcat = category->next; ++ ++ vcos_log_platform_unregister(category); ++ } ++ vcos_mutex_unlock(&lock); ++} ++ ++VCOSPRE_ const VCOS_LOG_CAT_T * VCOSPOST_ vcos_log_get_default_category(void) ++{ ++ return &dflt_log_category; ++} ++ ++void vcos_set_log_options(const char *opt) ++{ ++ (void)opt; ++} ++ ++void vcos_log_dump_mem_impl( const VCOS_LOG_CAT_T *cat, ++ const char *label, ++ uint32_t addr, ++ const void *voidMem, ++ size_t numBytes ) ++{ ++ const uint8_t *mem = (const uint8_t *)voidMem; ++ size_t offset; ++ char lineBuf[ 100 ]; ++ char *s; ++ ++ while ( numBytes > 0 ) ++ { ++ s = lineBuf; ++ ++ for ( offset = 0; offset < 16; offset++ ) ++ { ++ if ( offset < numBytes ) ++ { ++ s += vcos_snprintf( s, 4, "%02x ", mem[ offset ]); ++ } ++ else ++ { ++ s += vcos_snprintf( s, 4, " " ); ++ } ++ } ++ ++ for ( offset = 0; offset < 16; offset++ ) ++ { ++ if ( offset < numBytes ) ++ { ++ uint8_t ch = mem[ offset ]; ++ ++ if (( ch < ' ' ) || ( ch > '~' )) ++ { ++ ch = '.'; ++ } ++ *s++ = (char)ch; ++ } ++ } ++ *s++ = '\0'; ++ ++ if (( label != NULL ) && ( *label != '\0' )) ++ { ++ vcos_log_impl( cat, VCOS_LOG_INFO, "%s: %08x: %s", label, addr, lineBuf ); ++ } ++ else ++ { ++ vcos_log_impl( cat, VCOS_LOG_INFO, "%08x: %s", addr, lineBuf ); ++ } ++ ++ addr += 16; ++ mem += 16; ++ if ( numBytes > 16 ) ++ { ++ numBytes -= 16; ++ } ++ else ++ { ++ numBytes = 0; ++ } ++ } ++ ++} ++ ++void vcos_log_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, ...) ++{ ++ va_list ap; ++ va_start(ap,fmt); ++ vcos_vlog_impl( cat, _level, fmt, ap ); ++ va_end(ap); ++} ++ ++void vcos_vlog_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args) ++{ ++ vcos_vlog_impl_func( cat, _level, fmt, args ); ++} ++ ++void vcos_set_vlog_impl( VCOS_VLOG_IMPL_FUNC_T vlog_impl_func ) ++{ ++ if ( vlog_impl_func == NULL ) ++ { ++ vcos_vlog_impl_func = vcos_vlog_default_impl; ++ } ++ else ++ { ++ vcos_vlog_impl_func = vlog_impl_func; ++ } ++} ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.c +@@ -0,0 +1,73 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : vcos ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - memory alloc implementation ++=============================================================================*/ ++ ++#include "interface/vcos/vcos.h" ++ ++#ifndef _vcos_platform_malloc ++#include <stdlib.h> ++#define _vcos_platform_malloc malloc ++#define _vcos_platform_free free ++#endif ++ ++typedef struct malloc_header_s { ++ uint32_t guardword; ++ uint32_t size; ++ const char *description; ++ void *ptr; ++} MALLOC_HEADER_T; ++ ++ ++#define MIN_ALIGN sizeof(MALLOC_HEADER_T) ++ ++#define GUARDWORDHEAP 0xa55a5aa5 ++ ++void *vcos_generic_mem_alloc_aligned(VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *desc) ++{ ++ int local_align = align == 0 ? 1 : align; ++ int required_size = size + local_align + sizeof(MALLOC_HEADER_T); ++ void *ptr = _vcos_platform_malloc(required_size); ++ void *ret = (void *)VCOS_ALIGN_UP(((char *)ptr)+sizeof(MALLOC_HEADER_T), local_align); ++ MALLOC_HEADER_T *h = ((MALLOC_HEADER_T *)ret)-1; ++ ++ h->size = size; ++ h->description = desc; ++ h->guardword = GUARDWORDHEAP; ++ h->ptr = ptr; ++ ++ return ret; ++} ++ ++void *vcos_generic_mem_alloc(VCOS_UNSIGNED size, const char *desc) ++{ ++ return vcos_generic_mem_alloc_aligned(size,MIN_ALIGN,desc); ++} ++ ++void *vcos_generic_mem_calloc(VCOS_UNSIGNED count, VCOS_UNSIGNED sz, const char *desc) ++{ ++ uint32_t size = count*sz; ++ void *ptr = vcos_generic_mem_alloc_aligned(size,MIN_ALIGN,desc); ++ if (ptr) ++ { ++ memset(ptr, 0, size); ++ } ++ return ptr; ++} ++ ++void vcos_generic_mem_free(void *ptr) ++{ ++ MALLOC_HEADER_T *h; ++ if (! ptr) return; ++ ++ h = ((MALLOC_HEADER_T *)ptr)-1; ++ vcos_assert(h->guardword == GUARDWORDHEAP); ++ _vcos_platform_free(h->ptr); ++} ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.h +@@ -0,0 +1,54 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : VMCS Host Apps ++Module : Framework - VMCS ++ ++FILE DESCRIPTION ++Create the vcos_malloc API from the regular system malloc/free ++=============================================================================*/ ++ ++/** ++ * \file ++ * ++ * Create the vcos malloc API from a regular system malloc/free library. ++ * ++ * The API lets callers specify an alignment. ++ * ++ * Under VideoCore this is not needed, as we can simply use the rtos_malloc routines. ++ * But on host platforms that won't be the case. ++ * ++ */ ++ ++VCOSPRE_ void * VCOSPOST_ vcos_generic_mem_alloc(VCOS_UNSIGNED sz, const char *desc); ++VCOSPRE_ void * VCOSPOST_ vcos_generic_mem_calloc(VCOS_UNSIGNED count, VCOS_UNSIGNED sz, const char *descr); ++VCOSPRE_ void VCOSPOST_ vcos_generic_mem_free(void *ptr); ++VCOSPRE_ void * VCOSPOST_ vcos_generic_mem_alloc_aligned(VCOS_UNSIGNED sz, VCOS_UNSIGNED align, const char *desc); ++ ++#ifdef VCOS_INLINE_BODIES ++ ++VCOS_INLINE_IMPL ++void *vcos_malloc(VCOS_UNSIGNED size, const char *description) { ++ return vcos_generic_mem_alloc(size, description); ++} ++ ++VCOS_INLINE_IMPL ++void *vcos_calloc(VCOS_UNSIGNED num, VCOS_UNSIGNED size, const char *description) { ++ return vcos_generic_mem_calloc(num, size, description); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_free(void *ptr) { ++ vcos_generic_mem_free(ptr); ++} ++ ++VCOS_INLINE_IMPL ++void * vcos_malloc_aligned(VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *description) { ++ return vcos_generic_mem_alloc_aligned(size, align, description); ++} ++ ++ ++#endif /* VCOS_INLINE_BODIES */ ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_mutexes_are_reentrant.h +@@ -0,0 +1,68 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - reentrant mutexes mapped directly to regular ones ++=============================================================================*/ ++ ++#ifndef VCOS_GENERIC_REENTRANT_MUTEX_H ++#define VCOS_GENERIC_REENTRANT_MUTEX_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "interface/vcos/vcos_mutex.h" ++ ++/** ++ * \file ++ * ++ * Reentrant Mutexes directly using the native re-entrant mutex. ++ * ++ */ ++ ++typedef VCOS_MUTEX_T VCOS_REENTRANT_MUTEX_T; ++ ++/* Inline forwarding functions */ ++ ++#if defined(VCOS_INLINE_BODIES) ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_reentrant_mutex_create(VCOS_REENTRANT_MUTEX_T *m, const char *name) { ++ return vcos_mutex_create(m,name); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_reentrant_mutex_delete(VCOS_REENTRANT_MUTEX_T *m) { ++ vcos_mutex_delete(m); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_reentrant_mutex_lock(VCOS_REENTRANT_MUTEX_T *m) { ++ vcos_mutex_lock(m); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_reentrant_mutex_unlock(VCOS_REENTRANT_MUTEX_T *m) { ++ vcos_mutex_unlock(m); ++} ++ ++VCOS_INLINE_IMPL ++int vcos_reentrant_mutex_is_locked(VCOS_REENTRANT_MUTEX_T *m) { ++ return vcos_mutex_is_locked(m); ++} ++ ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++#endif ++ ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_thread_reaper.h +@@ -0,0 +1,35 @@ ++/*============================================================================= ++Copyright (c) 2010 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : vcos ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - thread reaping ++=============================================================================*/ ++ ++#ifndef VCOS_THREAD_REAPER_H ++#define VCOS_THREAD_REAPER_H ++ ++#define VCOS_HAVE_THREAD_REAPER ++ ++/** Initialise the thread reaper. ++ */ ++VCOS_STATUS_T vcos_thread_reaper_init(void); ++ ++/** Reap a thread. Arranges for the thread to be automatically ++ * joined. ++ * ++ * @sa vcos_thread_join(). ++ * ++ * @param thread the thread to terminate ++ * @param on_terminated called after the thread has exited ++ * @param cxt pass back to the callback ++ * ++ */ ++void vcos_thread_reap(VCOS_THREAD_T *thread, void (*on_terminated)(void*), void *cxt); ++ ++#endif ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/stdint.h +@@ -0,0 +1,17 @@ ++/*============================================================================= ++Copyright (c) 2010 Broadcom Europe Limited. ++All rights reserved. ++ ++FILE DESCRIPTION ++VideoCore OS fAbstraction Layer - stdint.h C standard header ++=============================================================================*/ ++ ++#ifndef _VCOS_PLATFORM_LINUX_STDINT_H ++#define _VCOS_PLATFORM_LINUX_STDINT_H ++ ++/* The Linux kernel does not have a <stdint.h> so we have to provide one of ++ our own. */ ++ ++#include <linux/types.h> /* includes integer types */ ++ ++#endif /* _VCOS_PLATFORM_LINUX_STDINT_H */ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel.c +@@ -0,0 +1,616 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : vcos ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - pthreads types ++=============================================================================*/ ++ ++#define VCOS_INLINE_BODIES ++#include <linux/module.h> ++#include <linux/kernel.h> ++#include <linux/time.h> ++#include <linux/pid.h> ++#include <linux/mm.h> ++#include <linux/version.h> ++ ++#if defined( CONFIG_BCM_KNLLOG_SUPPORT ) ++#include <linux/broadcom/knllog.h> ++#endif ++#include "interface/vcos/vcos.h" ++#ifdef HAVE_VCOS_VERSION ++#include "interface/vcos/vcos_build_info.h" ++#endif ++ ++VCOS_CFG_ENTRY_T vcos_cfg_dir; ++VCOS_CFG_ENTRY_T vcos_logging_cfg_dir; ++VCOS_CFG_ENTRY_T vcos_version_cfg; ++ ++#ifndef VCOS_DEFAULT_STACK_SIZE ++#define VCOS_DEFAULT_STACK_SIZE 4096 ++#endif ++ ++static VCOS_THREAD_ATTR_T default_attrs = { ++ 0, ++ VCOS_DEFAULT_STACK_SIZE, ++}; ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36) ++static DEFINE_SEMAPHORE(lock); ++#else ++static DECLARE_MUTEX(lock); ++#endif ++ ++typedef void (*LEGACY_ENTRY_FN_T)(int, void *); ++ ++/** Wrapper function around the real thread function. Posts the semaphore ++ * when completed. ++ */ ++static int vcos_thread_wrapper(void *arg) ++{ ++ void *ret; ++ VCOS_THREAD_T *thread = arg; ++ ++ vcos_assert(thread->magic == VCOS_THREAD_MAGIC); ++ ++ thread->thread.thread = current; ++ ++ vcos_add_thread(thread); ++ ++#ifdef VCOS_WANT_TLS_EMULATION ++ vcos_tls_thread_register(&thread->_tls); ++#endif ++ ++ if (thread->legacy) ++ { ++ LEGACY_ENTRY_FN_T fn = (LEGACY_ENTRY_FN_T)thread->entry; ++ fn(0,thread->arg); ++ ret = 0; ++ } ++ else ++ { ++ ret = thread->entry(thread->arg); ++ } ++ ++ thread->exit_data = ret; ++ ++ vcos_remove_thread(current); ++ ++ /* For join and cleanup */ ++ vcos_semaphore_post(&thread->wait); ++ ++ return 0; ++} ++ ++VCOS_STATUS_T vcos_thread_create(VCOS_THREAD_T *thread, ++ const char *name, ++ VCOS_THREAD_ATTR_T *attrs, ++ VCOS_THREAD_ENTRY_FN_T entry, ++ void *arg) ++{ ++ VCOS_STATUS_T st; ++ struct task_struct *kthread; ++ ++ memset(thread, 0, sizeof(*thread)); ++ thread->magic = VCOS_THREAD_MAGIC; ++ strlcpy( thread->name, name, sizeof( thread->name )); ++ thread->legacy = attrs ? attrs->legacy : 0; ++ thread->entry = entry; ++ thread->arg = arg; ++ ++ if (!name) ++ { ++ vcos_assert(0); ++ return VCOS_EINVAL; ++ } ++ ++ st = vcos_semaphore_create(&thread->wait, NULL, 0); ++ if (st != VCOS_SUCCESS) ++ { ++ return st; ++ } ++ ++ st = vcos_semaphore_create(&thread->suspend, NULL, 0); ++ if (st != VCOS_SUCCESS) ++ { ++ return st; ++ } ++ ++ /*required for event groups */ ++ vcos_timer_create(&thread->_timer.timer, thread->name, NULL, NULL); ++ ++ kthread = kthread_create((int (*)(void *))vcos_thread_wrapper, (void*)thread, name); ++ vcos_assert(kthread != NULL); ++ set_user_nice(kthread, attrs->ta_priority); ++ thread->thread.thread = kthread; ++ wake_up_process(kthread); ++ return VCOS_SUCCESS; ++} ++ ++void vcos_thread_join(VCOS_THREAD_T *thread, ++ void **pData) ++{ ++ vcos_assert(thread); ++ vcos_assert(thread->magic == VCOS_THREAD_MAGIC); ++ ++ thread->joined = 1; ++ ++ vcos_semaphore_wait(&thread->wait); ++ ++ if (pData) ++ { ++ *pData = thread->exit_data; ++ } ++ ++ /* Clean up */ ++ if (thread->stack) ++ vcos_free(thread->stack); ++ ++ vcos_semaphore_delete(&thread->wait); ++ vcos_semaphore_delete(&thread->suspend); ++ ++} ++ ++uint32_t vcos_getmicrosecs( void ) ++{ ++ struct timeval tv; ++/*XXX FIX ME! switch to ktime_get_ts to use MONOTONIC clock */ ++ do_gettimeofday(&tv); ++ return (tv.tv_sec*1000000) + tv.tv_usec; ++} ++ ++VCOS_STATUS_T vcos_timer_init(void) ++{ ++ return VCOS_SUCCESS; ++} ++ ++static const char *log_prefix[] = ++{ ++ "", /* VCOS_LOG_UNINITIALIZED */ ++ "", /* VCOS_LOG_NEVER */ ++ KERN_ERR, /* VCOS_LOG_ERROR */ ++ KERN_WARNING, /* VCOS_LOG_WARN */ ++ KERN_INFO, /* VCOS_LOG_INFO */ ++ KERN_INFO /* VCOS_LOG_TRACE */ ++}; ++ ++void vcos_vlog_default_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args) ++{ ++ char *newline = strchr( fmt, '\n' ); ++ const char *prefix; ++ const char *real_fmt; ++ ++ preempt_disable(); ++ { ++ if ( *fmt == '<' ) ++ { ++ prefix = fmt; ++ real_fmt= &fmt[3]; ++ } ++ else ++ { ++ prefix = log_prefix[_level]; ++ real_fmt = fmt; ++ } ++#if defined( CONFIG_BCM_KNLLOG_SUPPORT ) ++ knllog_ventry( "vcos", real_fmt, args ); ++#endif ++ printk( "%.3svcos: [%d]: ", prefix, current->pid ); ++ vprintk( real_fmt, args ); ++ ++ if ( newline == NULL ) ++ { ++ printk("\n"); ++ } ++ } ++ preempt_enable(); ++} ++ ++ ++const char * _vcos_log_level(void) ++{ ++ return NULL; ++} ++ ++/***************************************************************************** ++* ++* Displays the version information in /proc/vcos/version ++* ++*****************************************************************************/ ++ ++#ifdef HAVE_VCOS_VERSION ++ ++static void show_version( VCOS_CFG_BUF_T buf, void *data ) ++{ ++ static const char* copyright = "Copyright (c) 2011 Broadcom"; ++ ++ vcos_cfg_buf_printf( buf, "Built %s %s on %s\n%s\nversion %s\n", ++ vcos_get_build_date(), ++ vcos_get_build_time(), ++ vcos_get_build_hostname(), ++ copyright, ++ vcos_get_build_version() ); ++} ++ ++#endif ++ ++/***************************************************************************** ++* ++* Initialises vcos ++* ++*****************************************************************************/ ++ ++VCOS_STATUS_T vcos_init(void) ++{ ++ if ( vcos_cfg_mkdir( &vcos_cfg_dir, NULL, "vcos" ) != VCOS_SUCCESS ) ++ { ++ printk( KERN_ERR "%s: Unable to create vcos cfg entry\n", __func__ ); ++ } ++ vcos_logging_init(); ++ ++#ifdef HAVE_VCOS_VERSION ++ if ( vcos_cfg_create_entry( &vcos_version_cfg, &vcos_cfg_dir, "version", ++ show_version, NULL, NULL ) != VCOS_SUCCESS ) ++ { ++ printk( KERN_ERR "%s: Unable to create vcos cfg entry 'version'\n", __func__ ); ++ } ++#endif ++ ++ return VCOS_SUCCESS; ++} ++ ++/***************************************************************************** ++* ++* Deinitializes vcos ++* ++*****************************************************************************/ ++ ++void vcos_deinit(void) ++{ ++#ifdef HAVE_VCOS_VERSION ++ vcos_cfg_remove_entry( &vcos_version_cfg ); ++#endif ++ vcos_cfg_remove_entry( &vcos_cfg_dir ); ++} ++ ++void vcos_global_lock(void) ++{ ++ down(&lock); ++} ++ ++void vcos_global_unlock(void) ++{ ++ up(&lock); ++} ++ ++/* vcos_thread_exit() doesn't really stop this thread here ++ * ++ * At the moment, call to do_exit() will leak task_struct for ++ * current thread, so we let the vcos_thread_wrapper() do the ++ * cleanup and exit job, and we return w/o actually stopping the thread. ++ * ++ * ToDo: Kernel v2.6.31 onwards, it is considered safe to call do_exit() ++ * from kthread, the implementation of which is combined in 2 patches ++ * with commit-ids "63706172" and "cdd140bd" in oss Linux kernel tree ++ */ ++ ++void vcos_thread_exit(void *arg) ++{ ++ VCOS_THREAD_T *thread = vcos_thread_current(); ++ ++ vcos_assert(thread); ++ vcos_assert(thread->magic == VCOS_THREAD_MAGIC); ++ ++ thread->exit_data = arg; ++} ++ ++void vcos_thread_attr_init(VCOS_THREAD_ATTR_T *attrs) ++{ ++ *attrs = default_attrs; ++} ++ ++void _vcos_task_timer_set(void (*pfn)(void *), void *cxt, VCOS_UNSIGNED ms) ++{ ++ VCOS_THREAD_T *self = vcos_thread_current(); ++ vcos_assert(self); ++ vcos_assert(self->_timer.pfn == NULL); ++ ++ vcos_timer_create( &self->_timer.timer, "TaskTimer", pfn, cxt ); ++ vcos_timer_set(&self->_timer.timer, ms); ++} ++ ++void _vcos_task_timer_cancel(void) ++{ ++ VCOS_THREAD_T *self = vcos_thread_current(); ++ if (self->_timer.timer.linux_timer.function) ++ { ++ vcos_timer_cancel(&self->_timer.timer); ++ vcos_timer_delete(&self->_timer.timer); ++ } ++} ++ ++int vcos_vsnprintf( char *buf, size_t buflen, const char *fmt, va_list ap ) ++{ ++ return vsnprintf( buf, buflen, fmt, ap ); ++} ++ ++int vcos_snprintf(char *buf, size_t buflen, const char *fmt, ...) ++{ ++ int ret; ++ va_list ap; ++ va_start(ap,fmt); ++ ret = vsnprintf(buf, buflen, fmt, ap); ++ va_end(ap); ++ return ret; ++} ++ ++int vcos_llthread_running(VCOS_LLTHREAD_T *t) { ++ vcos_assert(0); /* this function only exists as a nasty hack for the video codecs! */ ++ return 1; ++} ++ ++static int vcos_verify_bkpts = 1; ++ ++int vcos_verify_bkpts_enabled(void) ++{ ++ return vcos_verify_bkpts; ++} ++ ++/***************************************************************************** ++* ++* _vcos_log_platform_init is called from vcos_logging_init ++* ++*****************************************************************************/ ++ ++void _vcos_log_platform_init(void) ++{ ++ if ( vcos_cfg_mkdir( &vcos_logging_cfg_dir, &vcos_cfg_dir, "logging" ) != VCOS_SUCCESS ) ++ { ++ printk( KERN_ERR "%s: Unable to create logging cfg entry\n", __func__ ); ++ } ++} ++ ++/***************************************************************************** ++* ++* Called to display the contents of a logging category. ++* ++*****************************************************************************/ ++ ++static void logging_show_category( VCOS_CFG_BUF_T buf, void *data ) ++{ ++ VCOS_LOG_CAT_T *category = data; ++ ++ vcos_cfg_buf_printf( buf, "%s\n", vcos_log_level_to_string( category->level )); ++} ++ ++/***************************************************************************** ++* ++* Called to parse content for a logging category. ++* ++*****************************************************************************/ ++ ++static void logging_parse_category( VCOS_CFG_BUF_T buf, void *data ) ++{ ++ VCOS_LOG_CAT_T *category = data; ++ const char *str = vcos_cfg_buf_get_str( buf ); ++ VCOS_LOG_LEVEL_T level; ++ ++ if ( vcos_string_to_log_level( str, &level ) == VCOS_SUCCESS ) ++ { ++ category->level = level; ++ } ++ else ++ { ++ printk( KERN_ERR "%s: Unrecognized logging level: '%s'\n", ++ __func__, str ); ++ } ++} ++ ++/***************************************************************************** ++* ++* _vcos_log_platform_register is called from vcos_log_register whenever ++* a new category is registered. ++* ++*****************************************************************************/ ++ ++void _vcos_log_platform_register(VCOS_LOG_CAT_T *category) ++{ ++ VCOS_CFG_ENTRY_T entry; ++ ++ if ( vcos_cfg_create_entry( &entry, &vcos_logging_cfg_dir, category->name, ++ logging_show_category, logging_parse_category, ++ category ) != VCOS_SUCCESS ) ++ { ++ printk( KERN_ERR "%s: Unable to create cfg entry for logging category '%s'\n", ++ __func__, category->name ); ++ category->platform_data = NULL; ++ } ++ else ++ { ++ category->platform_data = entry; ++ } ++} ++ ++/***************************************************************************** ++* ++* _vcos_log_platform_unregister is called from vcos_log_unregister whenever ++* a new category is unregistered. ++* ++*****************************************************************************/ ++ ++void _vcos_log_platform_unregister(VCOS_LOG_CAT_T *category) ++{ ++ VCOS_CFG_ENTRY_T entry; ++ ++ entry = category->platform_data; ++ if ( entry != NULL ) ++ { ++ if ( vcos_cfg_remove_entry( &entry ) != VCOS_SUCCESS ) ++ { ++ printk( KERN_ERR "%s: Unable to remove cfg entry for logging category '%s'\n", ++ __func__, category->name ); ++ } ++ } ++} ++ ++/***************************************************************************** ++* ++* Allocate memory. ++* ++*****************************************************************************/ ++ ++void *vcos_platform_malloc( VCOS_UNSIGNED required_size ) ++{ ++ if ( required_size >= ( 2 * PAGE_SIZE )) ++ { ++ /* For larger allocations, use vmalloc, whose underlying allocator ++ * returns pages ++ */ ++ ++ return vmalloc( required_size ); ++ } ++ ++ /* For smaller allocation, use kmalloc */ ++ ++ return kmalloc( required_size, GFP_KERNEL ); ++} ++ ++/***************************************************************************** ++* ++* Free previously allocated memory ++* ++*****************************************************************************/ ++ ++void vcos_platform_free( void *ptr ) ++{ ++ if (((unsigned long)ptr >= VMALLOC_START ) ++ && ((unsigned long)ptr < VMALLOC_END )) ++ { ++ vfree( ptr ); ++ } ++ else ++ { ++ kfree( ptr ); ++ } ++} ++ ++/***************************************************************************** ++* ++* Execute a routine exactly once. ++* ++*****************************************************************************/ ++ ++VCOS_STATUS_T vcos_once(VCOS_ONCE_T *once_control, ++ void (*init_routine)(void)) ++{ ++ /* In order to be thread-safe we need to re-test *once_control ++ * inside the lock. The outer test is basically an optimization ++ * so that once it is initialized we don't need to waste time ++ * trying to acquire the lock. ++ */ ++ ++ if ( *once_control == 0 ) ++ { ++ vcos_global_lock(); ++ if ( *once_control == 0 ) ++ { ++ init_routine(); ++ *once_control = 1; ++ } ++ vcos_global_unlock(); ++ } ++ ++ return VCOS_SUCCESS; ++} ++ ++/***************************************************************************** ++* ++* String duplication routine. ++* ++*****************************************************************************/ ++ ++char *vcos_strdup(const char *str) ++{ ++ return kstrdup(str, GFP_KERNEL); ++} ++ ++ ++/* Export functions for modules to use */ ++EXPORT_SYMBOL( vcos_init ); ++ ++EXPORT_SYMBOL( vcos_semaphore_trywait ); ++EXPORT_SYMBOL( vcos_semaphore_post ); ++EXPORT_SYMBOL( vcos_semaphore_create ); ++EXPORT_SYMBOL( vcos_semaphore_wait ); ++EXPORT_SYMBOL( vcos_semaphore_delete ); ++ ++EXPORT_SYMBOL( vcos_log_impl ); ++EXPORT_SYMBOL( vcos_vlog_impl ); ++EXPORT_SYMBOL( vcos_vlog_default_impl ); ++EXPORT_SYMBOL( vcos_log_get_default_category ); ++EXPORT_SYMBOL( vcos_log_register ); ++EXPORT_SYMBOL( vcos_log_unregister ); ++EXPORT_SYMBOL( vcos_logging_init ); ++EXPORT_SYMBOL( vcos_log_level_to_string ); ++EXPORT_SYMBOL( vcos_string_to_log_level ); ++EXPORT_SYMBOL( vcos_log_dump_mem_impl ); ++ ++EXPORT_SYMBOL( vcos_event_create ); ++EXPORT_SYMBOL( vcos_event_delete ); ++EXPORT_SYMBOL( vcos_event_flags_set ); ++EXPORT_SYMBOL( vcos_event_signal ); ++EXPORT_SYMBOL( vcos_event_wait ); ++EXPORT_SYMBOL( vcos_event_try ); ++ ++EXPORT_SYMBOL( vcos_getmicrosecs ); ++ ++EXPORT_SYMBOL( vcos_strcasecmp ); ++EXPORT_SYMBOL( vcos_snprintf ); ++EXPORT_SYMBOL( vcos_vsnprintf ); ++ ++EXPORT_SYMBOL( vcos_thread_current ); ++EXPORT_SYMBOL( vcos_thread_join ); ++EXPORT_SYMBOL( vcos_thread_create ); ++EXPORT_SYMBOL( vcos_thread_set_priority ); ++EXPORT_SYMBOL( vcos_thread_exit ); ++EXPORT_SYMBOL( vcos_once ); ++ ++EXPORT_SYMBOL( vcos_thread_attr_init ); ++EXPORT_SYMBOL( vcos_thread_attr_setpriority ); ++EXPORT_SYMBOL( vcos_thread_attr_settimeslice ); ++EXPORT_SYMBOL( vcos_thread_attr_setstacksize ); ++EXPORT_SYMBOL( _vcos_thread_attr_setlegacyapi ); ++ ++EXPORT_SYMBOL( vcos_event_flags_create ); ++EXPORT_SYMBOL( vcos_event_flags_delete ); ++EXPORT_SYMBOL( vcos_event_flags_get ); ++ ++EXPORT_SYMBOL( vcos_sleep ); ++ ++EXPORT_SYMBOL( vcos_calloc ); ++EXPORT_SYMBOL( vcos_malloc ); ++EXPORT_SYMBOL( vcos_malloc_aligned ); ++EXPORT_SYMBOL( vcos_free ); ++ ++EXPORT_SYMBOL( vcos_mutex_create ); ++EXPORT_SYMBOL( vcos_mutex_delete ); ++EXPORT_SYMBOL( vcos_mutex_lock ); ++EXPORT_SYMBOL( vcos_mutex_unlock ); ++EXPORT_SYMBOL( vcos_mutex_trylock ); ++ ++EXPORT_SYMBOL( vcos_timer_cancel ); ++EXPORT_SYMBOL( vcos_timer_create ); ++EXPORT_SYMBOL( vcos_timer_delete ); ++EXPORT_SYMBOL( vcos_timer_set ); ++ ++EXPORT_SYMBOL( vcos_atomic_flags_create ); ++EXPORT_SYMBOL( vcos_atomic_flags_delete ); ++EXPORT_SYMBOL( vcos_atomic_flags_or ); ++EXPORT_SYMBOL( vcos_atomic_flags_get_and_clear ); ++ ++EXPORT_SYMBOL( vcos_verify_bkpts_enabled ); ++ ++EXPORT_SYMBOL( vcos_strdup ); +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel_cfg.c +@@ -0,0 +1,332 @@ ++/***************************************************************************** ++* Copyright 2009 - 2010 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include "interface/vcos/vcos.h" ++#include <linux/module.h> ++#include <linux/proc_fs.h> ++#include <linux/seq_file.h> ++#include <asm/uaccess.h> ++ ++struct opaque_vcos_cfg_buf_t ++{ ++ struct seq_file *seq; ++ char *charBuf; ++}; ++ ++struct opaque_vcos_cfg_entry_t ++{ ++ struct proc_dir_entry *pde; ++ struct proc_dir_entry *parent_pde; ++ VCOS_CFG_SHOW_FPTR showFunc; ++ VCOS_CFG_PARSE_FPTR parseFunc; ++ void *data; ++ const char *name; ++}; ++ ++/***************************************************************************** ++* ++* cfg_proc_show ++* ++*****************************************************************************/ ++ ++static int cfg_proc_show( struct seq_file *s, void *v ) ++{ ++ VCOS_CFG_ENTRY_T entry; ++ struct opaque_vcos_cfg_buf_t buf; ++ ++ entry = s->private; ++ ++ if ( entry->showFunc ) ++ { ++ memset( &buf, 0, sizeof( buf )); ++ buf.seq = s; ++ ++ entry->showFunc( &buf, entry->data ); ++ } ++ ++ return 0; ++} ++ ++/***************************************************************************** ++* ++* cfg_proc_write ++* ++*****************************************************************************/ ++ ++static ssize_t cfg_proc_write( struct file *file, const char __user *buffer, size_t count, loff_t *ppos) ++{ ++ VCOS_CFG_ENTRY_T entry = PDE(file->f_path.dentry->d_inode)->data; ++ char *charBuf; ++ struct opaque_vcos_cfg_buf_t buf; ++ size_t len; ++ ++ if ( entry->parseFunc != NULL ) ++ { ++ /* The number 4000 is rather arbitrary. It just needs to be bigger than any input ++ * string we expect to use. ++ */ ++ ++ len = count; ++ if ( count > 4000 ) ++ { ++ len = 4000; ++ } ++ ++ /* Allocate a kernel buffer to contain the string being written. */ ++ ++ charBuf = kmalloc( len + 1, GFP_KERNEL ); ++ if ( copy_from_user( charBuf, buffer, len )) ++ { ++ kfree( charBuf ); ++ return -EFAULT; ++ } ++ ++ /* echo puts a trailing newline in the buffer - strip it out. */ ++ ++ if (( len > 0 ) && ( charBuf[ len - 1 ] == '\n' )) ++ { ++ len--; ++ } ++ charBuf[len] = '\0'; ++ ++ memset( &buf, 0, sizeof( buf )); ++ buf.charBuf = charBuf; ++ ++ entry->parseFunc( &buf, entry->data ); ++ kfree( charBuf ); ++ } ++ return count; ++} ++ ++/***************************************************************************** ++* ++* cfg_proc_open ++* ++*****************************************************************************/ ++ ++static int cfg_proc_open( struct inode *inode, struct file *file ) ++{ ++ return single_open( file, cfg_proc_show, PDE(inode)->data ); ++} ++ ++static const struct file_operations cfg_proc_fops = ++{ ++ .open = cfg_proc_open, ++ .read = seq_read, ++ .llseek = seq_lseek, ++ .release = single_release, ++ .write = cfg_proc_write, ++}; ++ ++/***************************************************************************** ++* ++* vcos_cfg_mkdir ++* ++*****************************************************************************/ ++ ++VCOS_STATUS_T vcos_cfg_mkdir( VCOS_CFG_ENTRY_T *entryp, ++ VCOS_CFG_ENTRY_T *parent, ++ const char *dirName ) ++{ ++ VCOS_CFG_ENTRY_T entry; ++ ++ if (( entry = kzalloc( sizeof( *entry ), GFP_KERNEL )) == NULL ) ++ { ++ return VCOS_ENOMEM; ++ } ++ ++ if ( parent == NULL ) ++ { ++ entry->pde = proc_mkdir( dirName, NULL ); ++ } ++ else ++ { ++ entry->pde = proc_mkdir( dirName, (*parent)->pde ); ++ entry->parent_pde = (*parent)->pde; ++ } ++ if ( entry->pde == NULL ) ++ { ++ kfree( entry ); ++ return VCOS_ENOMEM; ++ } ++ ++ entry->name = dirName; ++ ++ *entryp = entry; ++ return VCOS_SUCCESS; ++} ++ ++/***************************************************************************** ++* ++* vcos_cfg_create_entry ++* ++*****************************************************************************/ ++ ++VCOS_STATUS_T vcos_cfg_create_entry( VCOS_CFG_ENTRY_T *entryp, ++ VCOS_CFG_ENTRY_T *parent, ++ const char *entryName, ++ VCOS_CFG_SHOW_FPTR showFunc, ++ VCOS_CFG_PARSE_FPTR parseFunc, ++ void *data ) ++{ ++ VCOS_CFG_ENTRY_T entry; ++ mode_t mode; ++ ++ *entryp = NULL; ++ ++ if (( entry = kzalloc( sizeof( *entry ), GFP_KERNEL )) == NULL ) ++ { ++ return VCOS_ENOMEM; ++ } ++ ++ mode = 0; ++ if ( showFunc != NULL ) ++ { ++ mode |= 0444; ++ } ++ if ( parseFunc != NULL ) ++ { ++ mode |= 0200; ++ } ++ ++ if ( parent == NULL ) ++ { ++ entry->pde = create_proc_entry( entryName, mode, NULL ); ++ } ++ else ++ { ++ entry->pde = create_proc_entry( entryName, mode, (*parent)->pde ); ++ entry->parent_pde = (*parent)->pde; ++ } ++ if ( entry->pde == NULL ) ++ { ++ kfree( entry ); ++ return -ENOMEM; ++ } ++ entry->showFunc = showFunc; ++ entry->parseFunc = parseFunc; ++ entry->data = data; ++ entry->name = entryName; ++ ++ entry->pde->data = entry; ++ entry->pde->proc_fops = &cfg_proc_fops; ++ ++ *entryp = entry; ++ return VCOS_SUCCESS; ++} ++ ++/***************************************************************************** ++* ++* vcos_cfg_remove_entry ++* ++*****************************************************************************/ ++ ++VCOS_STATUS_T vcos_cfg_remove_entry( VCOS_CFG_ENTRY_T *entryp ) ++{ ++ if (( entryp != NULL ) && ( *entryp != NULL )) ++ { ++ remove_proc_entry( (*entryp)->name, (*entryp)->parent_pde ); ++ ++ kfree( *entryp ); ++ *entryp = NULL; ++ } ++ ++ return VCOS_SUCCESS; ++} ++ ++/***************************************************************************** ++* ++* vcos_cfg_is_entry_created ++* ++*****************************************************************************/ ++ ++int vcos_cfg_is_entry_created( VCOS_CFG_ENTRY_T entry ) ++{ ++ return ( entry != NULL ) && ( entry->pde != NULL ); ++} ++ ++/***************************************************************************** ++* ++* vcos_cfg_buf_printf ++* ++*****************************************************************************/ ++ ++void vcos_cfg_buf_printf( VCOS_CFG_BUF_T buf, const char *fmt, ... ) ++{ ++ struct seq_file *m = buf->seq; ++ ++ /* Bah - there is no seq_vprintf */ ++ ++ va_list args; ++ int len; ++ ++ if (m->count < m->size) { ++ va_start(args, fmt); ++ len = vsnprintf(m->buf + m->count, m->size - m->count, fmt, args); ++ va_end(args); ++ if (m->count + len < m->size) { ++ m->count += len; ++ return; ++ } ++ } ++ m->count = m->size; ++} ++ ++/***************************************************************************** ++* ++* vcos_cfg_buf_get_str ++* ++*****************************************************************************/ ++ ++char *vcos_cfg_buf_get_str( VCOS_CFG_BUF_T buf ) ++{ ++ return buf->charBuf; ++} ++ ++/***************************************************************************** ++* ++* vcos_cfg_get_proc_entry ++* ++* This function is only created for a couple of backwards compatibility ' ++* issues and shouldn't normally be used. ++* ++*****************************************************************************/ ++ ++void *vcos_cfg_get_proc_entry( VCOS_CFG_ENTRY_T entry ) ++{ ++ return entry->pde; ++} ++ ++/***************************************************************************** ++* ++* vcos_cfg_get_entry_name ++* ++*****************************************************************************/ ++ ++const char *vcos_cfg_get_entry_name( VCOS_CFG_ENTRY_T entry ) ++{ ++ return entry->pde->name; ++} ++ ++ ++EXPORT_SYMBOL( vcos_cfg_mkdir ); ++EXPORT_SYMBOL( vcos_cfg_create_entry ); ++EXPORT_SYMBOL( vcos_cfg_remove_entry ); ++EXPORT_SYMBOL( vcos_cfg_get_entry_name ); ++EXPORT_SYMBOL( vcos_cfg_is_entry_created ); ++EXPORT_SYMBOL( vcos_cfg_buf_printf ); ++EXPORT_SYMBOL( vcos_cfg_buf_get_str ); ++ ++EXPORT_SYMBOL_GPL( vcos_cfg_get_proc_entry ); ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel_misc.c +@@ -0,0 +1,113 @@ ++// ############################################################################# ++// START ####################################################################### ++/***************************************************************************** ++* Copyright 2009 - 2010 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include "interface/vcos/vcos.h" ++#include <linux/sched.h> ++#include <linux/module.h> ++#include <linux/freezer.h> ++#include <linux/string.h> ++#include <linux/slab.h> ++ ++/***************************************************************************** ++* ++* vcos_semaphore_wait_freezable ++* ++*****************************************************************************/ ++ ++VCOS_STATUS_T vcos_semaphore_wait_freezable(VCOS_SEMAPHORE_T *sem) ++{ ++ int rval, sig_pended = 0; ++ unsigned long flags; ++ struct task_struct *task = current; ++ ++ while (1) { ++ rval = down_interruptible((struct semaphore *)sem); ++ if (rval == 0) { /* down now */ ++ break; ++ } else { ++ if (freezing(current)) { ++ try_to_freeze(); ++ } else { ++ spin_lock_irqsave(&task->sighand->siglock, flags); ++ if (test_tsk_thread_flag(task, TIF_SIGPENDING)) { ++ clear_tsk_thread_flag(task, TIF_SIGPENDING); ++ sig_pended = 1; ++ } ++ spin_unlock_irqrestore(&task->sighand->siglock, flags); ++ } ++ } ++ } ++ ++ if (sig_pended) { ++ spin_lock_irqsave(&task->sighand->siglock, flags); ++ set_tsk_thread_flag(task, TIF_SIGPENDING); ++ spin_unlock_irqrestore(&task->sighand->siglock, flags); ++ } ++ ++ return 0; ++} ++ ++EXPORT_SYMBOL( vcos_semaphore_wait_freezable ); ++ ++/***************************************************************************** ++* ++* vcos_kmalloc ++* ++* We really need to convert malloc to do kmalloc or vmalloc based on the ++* size, but for now we'll add a separate function. ++* ++*****************************************************************************/ ++ ++void *vcos_kmalloc(VCOS_UNSIGNED size, const char *description) ++{ ++ (void)description; ++ ++ return kmalloc( size, GFP_KERNEL ); ++} ++ ++/***************************************************************************** ++* ++* vcos_kmalloc ++* ++* We really need to convert malloc to do kmalloc or vmalloc based on the ++* size, but for now we'll add a separate function. ++* ++*****************************************************************************/ ++ ++void *vcos_kcalloc(VCOS_UNSIGNED num, VCOS_UNSIGNED size, const char *description) ++{ ++ (void)description; ++ ++ return kzalloc( num * size, GFP_KERNEL ); ++} ++ ++/***************************************************************************** ++* ++* vcos_kfree ++* ++*****************************************************************************/ ++ ++void vcos_kfree(void *ptr) ++{ ++ kfree( ptr ); ++} ++ ++EXPORT_SYMBOL( vcos_kmalloc ); ++EXPORT_SYMBOL( vcos_kcalloc ); ++EXPORT_SYMBOL( vcos_kfree ); ++ ++// END ######################################################################### ++// ############################################################################# +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_mod_init.c +@@ -0,0 +1,64 @@ ++/***************************************************************************** ++* Copyright 2006 - 2008 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++****************************************************************************/ ++ ++/* ---- Include Files ---------------------------------------------------- */ ++ ++#include "interface/vcos/vcos.h" ++#include <linux/module.h> ++ ++/* ---- Public Variables ------------------------------------------------- */ ++ ++/* ---- Private Constants and Types -------------------------------------- */ ++ ++/* ---- Private Variables ------------------------------------------------ */ ++ ++/* ---- Private Function Prototypes -------------------------------------- */ ++ ++/* ---- Functions -------------------------------------------------------- */ ++ ++/**************************************************************************** ++* ++* Called to perform module initialization when the module is loaded ++* ++***************************************************************************/ ++ ++static int __init vcos_mod_init( void ) ++{ ++ printk( KERN_INFO "VCOS Module\n" ); ++ ++ vcos_init(); ++ return 0; ++} ++ ++/**************************************************************************** ++* ++* Called to perform module cleanup when the module is unloaded. ++* ++***************************************************************************/ ++ ++static void __exit vcos_mod_exit( void ) ++{ ++ vcos_deinit(); ++} ++ ++/****************************************************************************/ ++ ++module_init( vcos_mod_init ); ++module_exit( vcos_mod_exit ); ++ ++MODULE_AUTHOR("Broadcom"); ++MODULE_DESCRIPTION( "VCOS Module Functions" ); ++MODULE_LICENSE( "GPL" ); ++MODULE_VERSION( "1.0" ); ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform.h +@@ -0,0 +1,496 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : vcos ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - Linux kernel (partial) implementation. ++=============================================================================*/ ++ ++/* Do not include this file directly - instead include it via vcos.h */ ++ ++/** @file ++ * ++ * Linux kernel (partial) implementation of VCOS. ++ * ++ */ ++ ++#ifndef VCOS_PLATFORM_H ++#define VCOS_PLATFORM_H ++ ++#include <linux/types.h> ++#include <linux/semaphore.h> ++#include <linux/mutex.h> ++#include <asm/bitops.h> ++#include <linux/kthread.h> ++#include <linux/wait.h> ++#include <linux/vmalloc.h> ++#include <linux/jiffies.h> ++#include <linux/delay.h> ++#include <linux/string.h> ++#include <linux/types.h> ++#include <linux/interrupt.h> ++#include <linux/random.h> ++#include <linux/sched.h> ++#include <linux/ctype.h> ++#include <linux/uaccess.h> ++#include <linux/time.h> /* for time_t */ ++#include <linux/slab.h> ++#include <linux/vmalloc.h> ++ ++#define VCOS_HAVE_RTOS 1 ++#define VCOS_HAVE_SEMAPHORE 1 ++#define VCOS_HAVE_EVENT 1 ++#define VCOS_HAVE_QUEUE 0 ++#define VCOS_HAVE_LEGACY_ISR 0 ++#define VCOS_HAVE_TIMER 1 ++#define VCOS_HAVE_CANCELLATION_SAFE_TIMER 0 ++#define VCOS_HAVE_MEMPOOL 0 ++#define VCOS_HAVE_ISR 0 ++#define VCOS_HAVE_ATOMIC_FLAGS 1 ++#define VCOS_HAVE_BLOCK_POOL 0 ++#define VCOS_HAVE_ONCE 1 ++#define VCOS_HAVE_FILE 0 ++#define VCOS_HAVE_USER_BUF 0 ++#define VCOS_HAVE_CFG 1 ++#define VCOS_HAVE_SPINLOCK 0 ++#define VCOS_HAVE_CMD 1 ++#define VCOS_HAVE_EVENT_FLAGS 1 ++ ++/* Exclude many VCOS classes which don't have predicates */ ++#define VCOS_TLS_H ++#define VCOS_NAMED_MUTEX_H ++#define VCOS_REENTRANT_MUTEX_H ++#define VCOS_NAMED_SEMAPHORE_H ++#define VCOS_QUICKSLOW_MUTEX_H ++/*#define VCOS_INIT_H */ ++/*#define VCOS_MEM_H */ ++/*#define VCOS_STRING_H */ ++ ++typedef struct semaphore VCOS_SEMAPHORE_T; ++typedef struct semaphore VCOS_EVENT_T; ++typedef struct mutex VCOS_MUTEX_T; ++typedef volatile int VCOS_ONCE_T; ++ ++typedef unsigned int VCOS_UNSIGNED; ++typedef unsigned int VCOS_OPTION; ++typedef atomic_t VCOS_ATOMIC_FLAGS_T; ++ ++typedef struct ++{ ++ struct timer_list linux_timer; ++ void *context; ++ void (*expiration_routine)(void *context); ++ ++} VCOS_TIMER_T; ++ ++typedef struct VCOS_LLTHREAD_T ++{ ++ struct task_struct *thread; /**< The thread itself */ ++ VCOS_SEMAPHORE_T suspend; /**< For support event groups and similar - a per thread semaphore */ ++} VCOS_LLTHREAD_T; ++ ++typedef enum ++{ ++ VCOS_O_RDONLY = 00000000, ++ VCOS_O_WRONLY = 00000001, ++ VCOS_O_RDWR = 00000002, ++ VCOS_O_TRUNC = 00001000, ++} VCOS_FILE_FLAGS_T; ++ ++typedef struct file *VCOS_FILE_T; ++ ++#define VCOS_SUSPEND -1 ++#define VCOS_NO_SUSPEND 0 ++ ++#define VCOS_START 1 ++#define VCOS_NO_START 0 ++ ++#define VCOS_THREAD_PRI_MIN -20 ++#define VCOS_THREAD_PRI_MAX 19 ++ ++#define VCOS_THREAD_PRI_INCREASE -1 ++#define VCOS_THREAD_PRI_HIGHEST VCOS_THREAD_PRI_MIN ++#define VCOS_THREAD_PRI_LOWEST VCOS_THREAD_PRI_MAX ++#define VCOS_THREAD_PRI_NORMAL ((VCOS_THREAD_PRI_MAX+VCOS_THREAD_PRI_MIN)/2) ++#define VCOS_THREAD_PRI_ABOVE_NORMAL (VCOS_THREAD_PRI_NORMAL + VCOS_THREAD_PRI_INCREASE) ++#define VCOS_THREAD_PRI_REALTIME VCOS_THREAD_PRI_HIGHEST ++ ++#define _VCOS_AFFINITY_DEFAULT 0 ++#define _VCOS_AFFINITY_CPU0 0 ++#define _VCOS_AFFINITY_CPU1 0 ++#define _VCOS_AFFINITY_MASK 0 ++#define VCOS_CAN_SET_STACK_ADDR 0 ++ ++#define VCOS_TICKS_PER_SECOND HZ ++ ++#include "interface/vcos/generic/vcos_generic_event_flags.h" ++#include "interface/vcos/generic/vcos_mem_from_malloc.h" ++#include "interface/vcos/generic/vcos_joinable_thread_from_plain.h" ++ ++/*********************************************************** ++ * ++ * Memory allcoation ++ * ++ ***********************************************************/ ++ ++#define _vcos_platform_malloc vcos_platform_malloc ++#define _vcos_platform_free vcos_platform_free ++ ++void *vcos_platform_malloc( VCOS_UNSIGNED required_size ); ++void vcos_platform_free( void *ptr ); ++ ++#if defined(VCOS_INLINE_BODIES) ++ ++#undef VCOS_ASSERT_LOGGING_DISABLE ++#define VCOS_ASSERT_LOGGING_DISABLE 1 ++ ++/*********************************************************** ++ * ++ * Counted Semaphores ++ * ++ ***********************************************************/ ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_semaphore_wait(VCOS_SEMAPHORE_T *sem) { ++ int ret = down_interruptible(sem); ++ if ( ret == 0 ) ++ /* Success */ ++ return VCOS_SUCCESS; ++ else if ( ret == -EINTR ) ++ /* Interrupted */ ++ return VCOS_EINTR; ++ else ++ /* Default (timeout) */ ++ return VCOS_EAGAIN; ++} ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_semaphore_trywait(VCOS_SEMAPHORE_T *sem) { ++ if (down_trylock(sem) != 0) ++ return VCOS_EAGAIN; ++ return VCOS_SUCCESS; ++} ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_semaphore_create(VCOS_SEMAPHORE_T *sem, ++ const char *name, ++ VCOS_UNSIGNED initial_count) { ++ sema_init(sem, initial_count); ++ return VCOS_SUCCESS; ++} ++ ++VCOS_INLINE_IMPL ++void vcos_semaphore_delete(VCOS_SEMAPHORE_T *sem) { ++} ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_semaphore_post(VCOS_SEMAPHORE_T *sem) { ++ up(sem); ++ return VCOS_SUCCESS; ++} ++ ++/*********************************************************** ++ * ++ * Threads ++ * ++ ***********************************************************/ ++ ++#include "vcos_thread_map.h" ++ ++VCOS_INLINE_IMPL ++VCOS_LLTHREAD_T *vcos_llthread_current(void) { ++ return &vcos_kthread_current()->thread; ++} ++ ++VCOS_INLINE_IMPL ++void vcos_llthread_resume(VCOS_LLTHREAD_T *thread) { ++ vcos_assert(0); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_sleep(uint32_t ms) { ++ msleep(ms); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_thread_set_priority(VCOS_THREAD_T *thread, VCOS_UNSIGNED p) { ++ /* not implemented */ ++} ++VCOS_INLINE_IMPL ++VCOS_UNSIGNED vcos_thread_get_priority(VCOS_THREAD_T *thread) { ++ /* not implemented */ ++ return 0; ++} ++ ++/*********************************************************** ++ * ++ * Miscellaneous ++ * ++ ***********************************************************/ ++ ++VCOS_INLINE_IMPL ++int vcos_strcasecmp(const char *s1, const char *s2) { ++ return strcasecmp(s1,s2); ++} ++ ++ ++/*********************************************************** ++ * ++ * Mutexes ++ * ++ ***********************************************************/ ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_mutex_create(VCOS_MUTEX_T *m, const char *name) { ++ mutex_init(m); ++ return VCOS_SUCCESS; ++} ++ ++VCOS_INLINE_IMPL ++void vcos_mutex_delete(VCOS_MUTEX_T *m) { ++} ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_mutex_lock(VCOS_MUTEX_T *m) { ++ int ret = mutex_lock_interruptible(m); ++ if ( ret == 0 ) ++ /* Success */ ++ return VCOS_SUCCESS; ++ else if ( ret == -EINTR ) ++ /* Interrupted */ ++ return VCOS_EINTR; ++ else ++ /* Default */ ++ return VCOS_EAGAIN; ++} ++ ++VCOS_INLINE_IMPL ++void vcos_mutex_unlock(VCOS_MUTEX_T *m) { ++ mutex_unlock(m); ++} ++ ++VCOS_INLINE_IMPL ++int vcos_mutex_is_locked(VCOS_MUTEX_T *m) { ++ if (mutex_trylock(m) != 0) ++ return 1; /* it was locked */ ++ mutex_unlock(m); ++ /* it wasn't locked */ ++ return 0; ++} ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_mutex_trylock(VCOS_MUTEX_T *m) { ++ if (mutex_trylock(m) == 0) ++ return VCOS_SUCCESS; ++ else ++ return VCOS_EAGAIN; ++} ++ ++/* For supporting event groups - per thread semaphore */ ++VCOS_INLINE_IMPL ++void _vcos_thread_sem_wait(void) { ++ VCOS_THREAD_T *t = vcos_thread_current(); ++ vcos_semaphore_wait(&t->suspend); ++} ++ ++VCOS_INLINE_IMPL ++void _vcos_thread_sem_post(VCOS_THREAD_T *target) { ++ vcos_semaphore_post(&target->suspend); ++} ++ ++/*********************************************************** ++ * ++ * Events ++ * ++ ***********************************************************/ ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_event_create(VCOS_EVENT_T *event, const char *debug_name) ++{ ++ sema_init(event, 0); ++ return VCOS_SUCCESS; ++} ++ ++VCOS_INLINE_IMPL ++void vcos_event_signal(VCOS_EVENT_T *event) ++{ ++ up(event); ++} ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_event_wait(VCOS_EVENT_T *event) ++{ ++ int ret = down_interruptible(event); ++ if ( ret == -EINTR ) ++ /* Interrupted */ ++ return VCOS_EINTR; ++ else if (ret != 0) ++ /* Default (timeout) */ ++ return VCOS_EAGAIN; ++ /* Emulate a maximum count of 1 by removing any extra upness */ ++ while (down_trylock(event) == 0) continue; ++ return VCOS_SUCCESS; ++} ++ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_event_try(VCOS_EVENT_T *event) ++{ ++ return (down_trylock(event) == 0) ? VCOS_SUCCESS : VCOS_EAGAIN; ++} ++ ++VCOS_INLINE_IMPL ++void vcos_event_delete(VCOS_EVENT_T *event) ++{ ++} ++ ++/*********************************************************** ++ * ++ * Timers ++ * ++ ***********************************************************/ ++ ++VCOS_INLINE_DECL ++void vcos_timer_linux_func(unsigned long data) ++{ ++ VCOS_TIMER_T *vcos_timer = (VCOS_TIMER_T *)data; ++ ++ vcos_timer->expiration_routine( vcos_timer->context ); ++} ++ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_timer_create(VCOS_TIMER_T *timer, ++ const char *name, ++ void (*expiration_routine)(void *context), ++ void *context) { ++ init_timer(&timer->linux_timer); ++ timer->linux_timer.data = (unsigned long)timer; ++ timer->linux_timer.function = vcos_timer_linux_func; ++ ++ timer->context = context; ++ timer->expiration_routine = expiration_routine; ++ ++ return VCOS_SUCCESS; ++} ++ ++VCOS_INLINE_IMPL ++void vcos_timer_set(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay_ms) { ++ timer->linux_timer.expires = jiffies + msecs_to_jiffies(delay_ms); ++ add_timer(&timer->linux_timer); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_timer_cancel(VCOS_TIMER_T *timer) { ++ del_timer(&timer->linux_timer); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_timer_reset(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay_ms) { ++ del_timer_sync(&timer->linux_timer); ++ timer->linux_timer.expires = jiffies + msecs_to_jiffies(delay_ms); ++ add_timer(&timer->linux_timer); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_timer_delete(VCOS_TIMER_T *timer) { ++ timer->context = NULL; ++ timer->expiration_routine = NULL; ++ timer->linux_timer.function = NULL; ++ timer->linux_timer.data = 0; ++ return; ++} ++ ++VCOS_INLINE_IMPL ++VCOS_UNSIGNED vcos_process_id_current(void) { ++ return (VCOS_UNSIGNED)current->pid; ++} ++ ++ ++VCOS_INLINE_IMPL ++int vcos_in_interrupt(void) { ++ return in_interrupt(); ++} ++ ++/*********************************************************** ++ * ++ * Atomic flags ++ * ++ ***********************************************************/ ++ ++VCOS_INLINE_IMPL ++VCOS_STATUS_T vcos_atomic_flags_create(VCOS_ATOMIC_FLAGS_T *atomic_flags) ++{ ++ atomic_set(atomic_flags, 0); ++ return VCOS_SUCCESS; ++} ++ ++VCOS_INLINE_IMPL ++void vcos_atomic_flags_or(VCOS_ATOMIC_FLAGS_T *atomic_flags, uint32_t flags) ++{ ++ uint32_t value; ++ do { ++ value = atomic_read(atomic_flags); ++ } while (atomic_cmpxchg(atomic_flags, value, value | flags) != value); ++} ++ ++VCOS_INLINE_IMPL ++uint32_t vcos_atomic_flags_get_and_clear(VCOS_ATOMIC_FLAGS_T *atomic_flags) ++{ ++ return atomic_xchg(atomic_flags, 0); ++} ++ ++VCOS_INLINE_IMPL ++void vcos_atomic_flags_delete(VCOS_ATOMIC_FLAGS_T *atomic_flags) ++{ ++} ++ ++#undef VCOS_ASSERT_LOGGING_DISABLE ++#define VCOS_ASSERT_LOGGING_DISABLE 0 ++ ++#endif /* VCOS_INLINE_BODIES */ ++ ++VCOS_INLINE_DECL void _vcos_thread_sem_wait(void); ++VCOS_INLINE_DECL void _vcos_thread_sem_post(VCOS_THREAD_T *); ++ ++/*********************************************************** ++ * ++ * Misc ++ * ++ ***********************************************************/ ++VCOS_INLINE_DECL char *vcos_strdup(const char *str); ++ ++/*********************************************************** ++ * ++ * Logging ++ * ++ ***********************************************************/ ++ ++VCOSPRE_ const char * VCOSPOST_ _vcos_log_level(void); ++#define _VCOS_LOG_LEVEL() _vcos_log_level() ++ ++#define vcos_log_platform_init() _vcos_log_platform_init() ++#define vcos_log_platform_register(category) _vcos_log_platform_register(category) ++#define vcos_log_platform_unregister(category) _vcos_log_platform_unregister(category) ++ ++struct VCOS_LOG_CAT_T; /* Forward declaration since vcos_logging.h hasn't been included yet */ ++ ++void _vcos_log_platform_init(void); ++void _vcos_log_platform_register(struct VCOS_LOG_CAT_T *category); ++void _vcos_log_platform_unregister(struct VCOS_LOG_CAT_T *category); ++ ++/*********************************************************** ++ * ++ * Memory barriers ++ * ++ ***********************************************************/ ++ ++#define vcos_wmb(x) wmb() ++#define vcos_rmb() rmb() ++ ++#include "interface/vcos/generic/vcos_common.h" ++/*#include "interface/vcos/generic/vcos_generic_quickslow_mutex.h" */ ++ ++#endif /* VCOS_PLATFORM_H */ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform_types.h +@@ -0,0 +1,47 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : osal ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - platform-specific types and defines ++=============================================================================*/ ++ ++#ifndef VCOS_PLATFORM_TYPES_H ++#define VCOS_PLATFORM_TYPES_H ++ ++#include <stddef.h> ++#include <linux/types.h> ++#include <linux/bug.h> ++ ++#define VCOSPRE_ extern ++#define VCOSPOST_ ++ ++#if defined(__GNUC__) && (( __GNUC__ > 2 ) || (( __GNUC__ == 2 ) && ( __GNUC_MINOR__ >= 3 ))) ++#define VCOS_FORMAT_ATTR_(ARCHETYPE, STRING_INDEX, FIRST_TO_CHECK) __attribute__ ((format (ARCHETYPE, STRING_INDEX, FIRST_TO_CHECK))) ++#else ++#define VCOS_FORMAT_ATTR_(ARCHETYPE, STRING_INDEX, FIRST_TO_CHECK) ++#endif ++ ++#if !defined( __STDC_VERSION__ ) ++#define __STDC_VERSION__ 199901L ++#endif ++ ++#if !defined( __STDC_VERSION ) ++#define __STDC_VERSION __STDC_VERSION__ ++#endif ++ ++static inline void __vcos_bkpt( void ) { BUG(); } ++#define VCOS_BKPT __vcos_bkpt() ++ ++#define VCOS_ASSERT_MSG(...) printk( KERN_ERR "vcos_assert: " __VA_ARGS__ ) ++ ++#define PRId64 "lld" ++#define PRIi64 "lli" ++#define PRIo64 "llo" ++#define PRIu64 "llu" ++#define PRIx64 "llx" ++ ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_thread_map.c +@@ -0,0 +1,129 @@ ++/***************************************************************************** ++* Copyright 2009 - 2010 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++/** Support to allow VCOS thread-related functions to be called from ++ * threads that were not created by VCOS. ++ */ ++ ++#include <linux/semaphore.h> ++#include <linux/vmalloc.h> ++#include <linux/list.h> ++#include <linux/sched.h> ++ ++#include "vcos_thread_map.h" ++#include "interface/vcos/vcos_logging.h" ++ ++/* ++ * Store the vcos_thread pointer at the end of ++ * current kthread stack, right after the thread_info ++ * structure. ++ * ++ * I belive we should be safe here to steal these 4 bytes ++ * from the stack, as long as the vcos thread does not use up ++ * all the stack available ++ * ++ * NOTE: This scheme will not work on architectures with stack growing up ++ */ ++ ++/* Shout, if we are not being compiled for ARM kernel */ ++ ++#ifndef CONFIG_ARM ++#error " **** The vcos kthread implementation may not work for non-ARM kernel ****" ++#endif ++ ++static inline void *to_current_vcos_thread(void) ++{ ++ unsigned long *vcos_data; ++ ++ vcos_data = (unsigned long *)((char *)current_thread_info() + sizeof(struct thread_info)); ++ ++ return (void *)vcos_data; ++} ++ ++ ++static inline void *to_vcos_thread(struct task_struct *tsk) ++{ ++ unsigned long *vcos_data; ++ ++ vcos_data = (unsigned long *)((char *)tsk->stack + sizeof(struct thread_info)); ++ ++ return (void *)vcos_data; ++} ++ ++/** ++ @fn uint32_t vcos_add_thread(THREAD_MAP_T *vcos_thread); ++*/ ++uint32_t vcos_add_thread(VCOS_THREAD_T *vcos_thread) ++{ ++ VCOS_THREAD_T **vcos_thread_storage = (VCOS_THREAD_T **)to_current_vcos_thread(); ++ ++ *vcos_thread_storage = vcos_thread; ++ ++ return(0); ++} ++ ++ ++/** ++ @fn uint32_t vcos_remove_thread(struct task_struct * thread_id); ++*/ ++uint32_t vcos_remove_thread(struct task_struct *thread_id) ++{ ++ /* Remove thread_id -> VCOS_THREAD_T relationship */ ++ VCOS_THREAD_T **vcos_thread_storage; ++ ++ /* ++ * We want to be able to build vcos as a loadable module, which ++ * means that we can't call get_task_struct. So we assert if we're ++ * ever called with thread_id != current. ++ */ ++ ++ BUG_ON( thread_id != current ); ++ ++ vcos_thread_storage = (VCOS_THREAD_T **)to_vcos_thread(thread_id); ++ ++ *(unsigned long *)vcos_thread_storage = 0xCAFEBABE; ++ ++ return(0); ++} ++ ++ ++VCOS_THREAD_T *vcos_kthread_current(void) ++{ ++ VCOS_THREAD_T **vcos_thread_storage = (VCOS_THREAD_T **)to_current_vcos_thread(); ++ ++ /* If we find this, either the thread is already dead or stack pages of a ++ * dead vcos thread are re-allocated to this one. ++ * ++ * Since there's no way to differentiate between these 2 cases, we just dump ++ * the current task name to the log. ++ * ++ * If the current thread is created using VCOS API, you should *never* see this ++ * print. ++ * ++ * If its a non-VCOS thread, just let it go ... ++ * ++ * To debug VCOS, uncomment printk's under the "if" condition below ++ * ++ */ ++ if (*vcos_thread_storage == (void *)0xCAFEBABE) ++ { ++ #if 0 ++ printk(KERN_DEBUG"****************************************************\n"); ++ printk(KERN_DEBUG"%s : You have a problem, if \"%s\" is a VCOS thread\n",__func__, current->comm); ++ printk(KERN_DEBUG"****************************************************\n"); ++ #endif ++ } ++ ++ return *vcos_thread_storage; ++} +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_thread_map.h +@@ -0,0 +1,39 @@ ++/***************************************************************************** ++* Copyright 2009 - 2010 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++ ++#ifndef VCOS_THREAD_MAP_H ++#define VCOS_THREAD_MAP_H ++ ++#include <linux/string.h> ++ ++#include "vcos_platform.h" ++ ++static inline void vcos_thread_map_init(void) ++{ ++ return; ++} ++ ++static inline void vcos_thread_map_cleanup(void) ++{ ++ return; ++} ++ ++uint32_t vcos_add_thread(VCOS_THREAD_T *vcos_thread); ++ ++uint32_t vcos_remove_thread(struct task_struct *thread_id); ++ ++VCOS_THREAD_T *vcos_kthread_current(void); ++ ++#endif /*VCOS_THREAD_MAP_H */ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos.h +@@ -0,0 +1,201 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - public header file ++=============================================================================*/ ++ ++/** ++ * \mainpage OS Abstraction Layer ++ * ++ * \section intro Introduction ++ * ++ * This abstraction layer is here to allow the underlying OS to be easily changed (e.g. from ++ * Nucleus to ThreadX) and to aid in porting host applications to new targets. ++ * ++ * \subsection error Error handling ++ * ++ * Wherever possible, VCOS functions assert internally and return void. The only exceptions ++ * are creation functions (which might fail due to lack of resources) and functions that ++ * might timeout or fail due to lack of space. Errors that might be reported by the underlying ++ * OS API (e.g. invalid mutex) are treated as a programming error, and are merely asserted on. ++ * ++ * \section thread_synch Threads and synchronisation ++ * ++ * \subsection thread Threads ++ * ++ * The thread API is somewhat different to that found in Nucleus. In particular, threads ++ * cannot just be destroyed at arbitrary times and nor can they merely exit. This is so ++ * that the same API can be implemented across all interesting platforms without too much ++ * difficulty. See vcos_thread.h for details. Thread attributes are configured via ++ * the VCOS_THREAD_ATTR_T structure, found in vcos_thread_attr.h. ++ * ++ * \subsection sema Semaphores ++ * ++ * Counted semaphores (c.f. Nucleus NU_SEMAPHORE) are created with VCOS_SEMAPHORE_T. ++ * Under ThreadX on VideoCore, semaphores are implemented using VideoCore spinlocks, and ++ * so are quite a lot faster than ordinary ThreadX semaphores. See vcos_semaphore.h. ++ * ++ * \subsection mtx Mutexes ++ * ++ * Mutexes are used for locking. Attempts to take a mutex twice, or to unlock it ++ * in a different thread to the one in which it was locked should be expected to fail. ++ * Mutexes are not re-entrant (see vcos_reentrant_mutex.h for a slightly slower ++ * re-entrant mutex). ++ * ++ * \subsection evflags Event flags ++ * ++ * Event flags (the ThreadX name - also known as event groups under Nucleus) provide ++ * 32 flags which can be waited on by multiple clients, and signalled by multiple clients. ++ * A timeout can be specified. See vcos_event_flags.h. An alternative to this is the ++ * VCOS_EVENT_T (see vcos_event.h) which is akin to the Win32 auto-reset event, or a ++ * saturating counted semaphore. ++ * ++ * \subsection event Events ++ * ++ * A VCOS_EVENT_T is a bit like a saturating semaphore. No matter how many times it ++ * is signalled, the waiter will only wake up once. See vcos_event.h. You might think this ++ * is useful if you suspect that the cost of reading the semaphore count (perhaps via a ++ * system call) is expensive on your platform. ++ * ++ * \subsection tls Thread local storage ++ * ++ * Thread local storage is supported using vcos_tls.h. This is emulated on Nucleus ++ * and ThreadX. ++ * ++ * \section int Interrupts ++ * ++ * The legacy LISR/HISR scheme found in Nucleus is supported via the legacy ISR API, ++ * which is also supported on ThreadX. New code should avoid this, and old code should ++ * be migrated away from it, since it is slow. See vcos_legacy_isr.h. ++ * ++ * Registering an interrupt handler, and disabling/restoring interrupts, is handled ++ * using the functions in vcos_isr.h. ++ * ++ */ ++ ++/** ++ * \file vcos.h ++ * ++ * This is the top level header file. Clients include this. It pulls in the platform-specific ++ * header file (vcos_platform.h) together with header files defining the expected APIs, such ++ * as vcos_mutex.h, vcos_semaphore.h, etc. It is also possible to include these header files ++ * directly. ++ * ++ */ ++ ++#ifndef VCOS_H ++#define VCOS_H ++ ++#include "interface/vcos/vcos_assert.h" ++#include "vcos_types.h" ++#include "vcos_platform.h" ++ ++#ifndef VCOS_INIT_H ++#include "interface/vcos/vcos_init.h" ++#endif ++ ++#ifndef VCOS_SEMAPHORE_H ++#include "interface/vcos/vcos_semaphore.h" ++#endif ++ ++#ifndef VCOS_THREAD_H ++#include "interface/vcos/vcos_thread.h" ++#endif ++ ++#ifndef VCOS_MUTEX_H ++#include "interface/vcos/vcos_mutex.h" ++#endif ++ ++#ifndef VCOS_MEM_H ++#include "interface/vcos/vcos_mem.h" ++#endif ++ ++#ifndef VCOS_LOGGING_H ++#include "interface/vcos/vcos_logging.h" ++#endif ++ ++#ifndef VCOS_STRING_H ++#include "interface/vcos/vcos_string.h" ++#endif ++ ++#ifndef VCOS_EVENT_H ++#include "interface/vcos/vcos_event.h" ++#endif ++ ++#ifndef VCOS_THREAD_ATTR_H ++#include "interface/vcos/vcos_thread_attr.h" ++#endif ++ ++#ifndef VCOS_TLS_H ++#include "interface/vcos/vcos_tls.h" ++#endif ++ ++#ifndef VCOS_REENTRANT_MUTEX_H ++#include "interface/vcos/vcos_reentrant_mutex.h" ++#endif ++ ++#ifndef VCOS_NAMED_SEMAPHORE_H ++#include "interface/vcos/vcos_named_semaphore.h" ++#endif ++ ++#ifndef VCOS_QUICKSLOW_MUTEX_H ++#include "interface/vcos/vcos_quickslow_mutex.h" ++#endif ++ ++/* Headers with predicates */ ++ ++#if VCOS_HAVE_EVENT_FLAGS ++#include "interface/vcos/vcos_event_flags.h" ++#endif ++ ++#if VCOS_HAVE_QUEUE ++#include "interface/vcos/vcos_queue.h" ++#endif ++ ++#if VCOS_HAVE_LEGACY_ISR ++#include "interface/vcos/vcos_legacy_isr.h" ++#endif ++ ++#if VCOS_HAVE_TIMER ++#include "interface/vcos/vcos_timer.h" ++#endif ++ ++#if VCOS_HAVE_MEMPOOL ++#include "interface/vcos/vcos_mempool.h" ++#endif ++ ++#if VCOS_HAVE_ISR ++#include "interface/vcos/vcos_isr.h" ++#endif ++ ++#if VCOS_HAVE_ATOMIC_FLAGS ++#include "interface/vcos/vcos_atomic_flags.h" ++#endif ++ ++#if VCOS_HAVE_ONCE ++#include "interface/vcos/vcos_once.h" ++#endif ++ ++#if VCOS_HAVE_BLOCK_POOL ++#include "interface/vcos/vcos_blockpool.h" ++#endif ++ ++#if VCOS_HAVE_FILE ++#include "interface/vcos/vcos_file.h" ++#endif ++ ++#if VCOS_HAVE_CFG ++#include "interface/vcos/vcos_cfg.h" ++#endif ++ ++#if VCOS_HAVE_CMD ++#include "interface/vcos/vcos_cmd.h" ++#endif ++ ++#endif /* VCOS_H */ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_assert.h +@@ -0,0 +1,269 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : osal ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - Assertion and error-handling macros. ++=============================================================================*/ ++ ++ ++#ifndef VCOS_ASSERT_H ++#define VCOS_ASSERT_H ++ ++/* ++ * Macro: ++ * vcos_assert(cond) ++ * vcos_assert_msg(cond, fmt, ...) ++ * Use: ++ * Detecting programming errors by ensuring that assumptions are correct. ++ * On failure: ++ * Performs a platform-dependent "breakpoint", usually with an assert-style ++ * message. The '_msg' variant expects a printf-style format string and ++ * parameters. ++ * If a failure is detected, the code should be fixed and rebuilt. ++ * In release builds: ++ * Generates no code, i.e. does not evaluate 'cond'. ++ * Returns: ++ * Nothing. ++ * ++ * Macro: ++ * vcos_demand(cond) ++ * vcos_demand_msg(cond, fmt, ...) ++ * Use: ++ * Detecting fatal system errors that require a reboot. ++ * On failure: ++ * Performs a platform-dependent "breakpoint", usually with an assert-style ++ * message, then calls vcos_abort (see below). ++ * In release builds: ++ * Calls vcos_abort() if 'cond' is false. ++ * Returns: ++ * Nothing (never, on failure). ++ * ++ * Macro: ++ * vcos_verify(cond) ++ * vcos_verify_msg(cond, fmt, ...) ++ * Use: ++ * Detecting run-time errors and interesting conditions, normally within an ++ * 'if' statement to catch the failures, i.e. ++ * if (!vcos_verify(cond)) handle_error(); ++ * On failure: ++ * Generates a message and optionally stops at a platform-dependent ++ * "breakpoint" (usually disabled). See vcos_verify_bkpts_enable below. ++ * In release builds: ++ * Just evaluates and returns 'cond'. ++ * Returns: ++ * Non-zero if 'cond' is true, otherwise zero. ++ * ++ * Macro: ++ * vcos_static_assert(cond) ++ * Use: ++ * Detecting compile-time errors. ++ * On failure: ++ * Generates a compiler error. ++ * In release builds: ++ * Generates a compiler error. ++ * ++ * Function: ++ * void vcos_abort(void) ++ * Use: ++ * Invokes the fatal error handling mechanism, alerting the host where ++ * applicable. ++ * Returns: ++ * Never. ++ * ++ * Macro: ++ * VCOS_VERIFY_BKPTS ++ * Use: ++ * Define in a module (before including vcos.h) to specify an alternative ++ * flag to control breakpoints on vcos_verify() failures. ++ * Returns: ++ * Non-zero values enable breakpoints. ++ * ++ * Function: ++ * int vcos_verify_bkpts_enable(int enable); ++ * Use: ++ * Sets the global flag controlling breakpoints on vcos_verify failures, ++ * enabling the breakpoints iff 'enable' is non-zero. ++ * Returns: ++ * The previous state of the flag. ++ * ++ * Function: ++ * int vcos_verify_bkpts_enabled(void); ++ * Use: ++ * Queries the state of the global flag enabling breakpoints on vcos_verify ++ * failures. ++ * Returns: ++ * The current state of the flag. ++ * ++ * Examples: ++ * ++ * int my_breakpoint_enable_flag = 1; ++ * ++ * #define VCOS_VERIFY_BKPTS my_breakpoint_enable_flag ++ * ++ * #include "interface/vcos/vcos.h" ++ * ++ * vcos_static_assert((sizeof(object) % 32) == 0); ++ * ++ * // ... ++ * ++ * vcos_assert_msg(postcondition_is_true, "Coding error"); ++ * ++ * if (!vcos_verify_msg(buf, "Buffer allocation failed (%d bytes)", size)) ++ * { ++ * // Tidy up ++ * // ... ++ * return OUT_OF_MEMORY; ++ * } ++ * ++ * vcos_demand(*p++==GUARDWORDHEAP); ++ */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++ ++#ifdef __COVERITY__ ++#undef VCOS_ASSERT_BKPT ++#define VCOS_ASSERT_BKPT __coverity_panic__() ++#endif ++ ++#ifndef VCOS_VERIFY_BKPTS ++#define VCOS_VERIFY_BKPTS vcos_verify_bkpts_enabled() ++#endif ++ ++#ifndef VCOS_BKPT ++#if defined(__VIDEOCORE__) && !defined(VCOS_ASSERT_NO_BKPTS) ++#define VCOS_BKPT _bkpt() ++#else ++#define VCOS_BKPT (void )0 ++#endif ++#endif ++ ++#ifndef VCOS_ASSERT_BKPT ++#define VCOS_ASSERT_BKPT VCOS_BKPT ++#endif ++ ++#ifndef VCOS_VERIFY_BKPT ++#define VCOS_VERIFY_BKPT (VCOS_VERIFY_BKPTS ? VCOS_BKPT : (void)0) ++#endif ++ ++VCOSPRE_ int VCOSPOST_ vcos_verify_bkpts_enabled(void); ++VCOSPRE_ int VCOSPOST_ vcos_verify_bkpts_enable(int enable); ++VCOSPRE_ void VCOSPOST_ vcos_abort(void); ++ ++#ifndef VCOS_ASSERT_MSG ++#ifdef LOGGING ++extern void logging_assert(const char *file, const char *func, int line, const char *format, ...); ++#define VCOS_ASSERT_MSG(...) ((VCOS_ASSERT_LOGGING && !VCOS_ASSERT_LOGGING_DISABLE) ? logging_assert(__FILE__, __func__, __LINE__, __VA_ARGS__) : (void)0) ++#else ++#define VCOS_ASSERT_MSG(...) ((void)0) ++#endif ++#endif ++ ++#ifndef VCOS_VERIFY_MSG ++#define VCOS_VERIFY_MSG(...) VCOS_ASSERT_MSG(__VA_ARGS__) ++#endif ++ ++#ifndef VCOS_ASSERT_LOGGING ++#define VCOS_ASSERT_LOGGING 0 ++#endif ++ ++#ifndef VCOS_ASSERT_LOGGING_DISABLE ++#define VCOS_ASSERT_LOGGING_DISABLE 0 ++#endif ++ ++#if !defined(NDEBUG) || defined(VCOS_RELEASE_ASSERTS) ++ ++#ifndef vcos_assert ++#define vcos_assert(cond) \ ++ ( (cond) ? (void)0 : (VCOS_ASSERT_MSG("%s", #cond), VCOS_ASSERT_BKPT) ) ++#endif ++ ++#ifndef vcos_assert_msg ++#define vcos_assert_msg(cond, ...) \ ++ ( (cond) ? (void)0 : (VCOS_ASSERT_MSG(__VA_ARGS__), VCOS_ASSERT_BKPT) ) ++#endif ++ ++#else /* !defined(NDEBUG) || defined(VCOS_RELEASE_ASSERTS) */ ++ ++#ifndef vcos_assert ++#define vcos_assert(cond) (void)0 ++#endif ++ ++#ifndef vcos_assert_msg ++#define vcos_assert_msg(cond, ...) (void)0 ++#endif ++ ++#endif /* !defined(NDEBUG) || defined(VCOS_RELEASE_ASSERTS) */ ++ ++#if !defined(NDEBUG) ++ ++#ifndef vcos_demand ++#define vcos_demand(cond) \ ++ ( (cond) ? (void)0 : (VCOS_ASSERT_MSG("%s", #cond), VCOS_ASSERT_BKPT, vcos_abort()) ) ++#endif ++ ++#ifndef vcos_demand_msg ++#define vcos_demand_msg(cond, ...) \ ++ ( (cond) ? (void)0 : (VCOS_ASSERT_MSG(__VA_ARGS__), VCOS_ASSERT_BKPT, vcos_abort()) ) ++#endif ++ ++#ifndef vcos_verify ++#define vcos_verify(cond) \ ++ ( (cond) ? 1 : (VCOS_VERIFY_MSG("%s", #cond), VCOS_VERIFY_BKPT, 0) ) ++#endif ++ ++#ifndef vcos_verify_msg ++#define vcos_verify_msg(cond, ...) \ ++ ( (cond) ? 1 : (VCOS_VERIFY_MSG(__VA_ARGS__), VCOS_VERIFY_BKPT, 0) ) ++#endif ++ ++#else /* !defined(NDEBUG) */ ++ ++#ifndef vcos_demand ++#define vcos_demand(cond) \ ++ ( (cond) ? (void)0 : vcos_abort() ) ++#endif ++ ++#ifndef vcos_demand_msg ++#define vcos_demand_msg(cond, ...) \ ++ ( (cond) ? (void)0 : vcos_abort() ) ++#endif ++ ++#ifndef vcos_verify ++#define vcos_verify(cond) (cond) ++#endif ++ ++#ifndef vcos_verify_msg ++#define vcos_verify_msg(cond, ...) (cond) ++#endif ++ ++#endif /* !defined(NDEBUG) */ ++ ++#ifndef vcos_static_assert ++#if defined(__GNUC__) ++#define vcos_static_assert(cond) __attribute__((unused)) extern int vcos_static_assert[(cond)?1:-1] ++#else ++#define vcos_static_assert(cond) extern int vcos_static_assert[(cond)?1:-1] ++#endif ++#endif ++ ++#ifndef vc_assert ++#define vc_assert(cond) vcos_assert(cond) ++#endif ++ ++/** Print out a backtrace, on supported platforms. ++ */ ++extern void vcos_backtrace_self(void); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif /* VCOS_ASSERT_H */ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_atomic_flags.h +@@ -0,0 +1,72 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver (just for consistency with the rest of vcos ;) ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - public header file ++=============================================================================*/ ++ ++#ifndef VCOS_ATOMIC_FLAGS_H ++#define VCOS_ATOMIC_FLAGS_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++/** ++ * \file vcos_atomic_flags.h ++ * ++ * Defines atomic flags API. ++ * ++ * 32 flags. Atomic "or" and "get and clear" operations ++ */ ++ ++/** ++ * Create an atomic flags instance. ++ * ++ * @param atomic_flags Pointer to atomic flags instance, filled in on return ++ * ++ * @return VCOS_SUCCESS if succeeded. ++ */ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_atomic_flags_create(VCOS_ATOMIC_FLAGS_T *atomic_flags); ++ ++/** ++ * Atomically set the specified flags. ++ * ++ * @param atomic_flags Instance to set flags on ++ * @param flags Mask of flags to set ++ */ ++VCOS_INLINE_DECL ++void vcos_atomic_flags_or(VCOS_ATOMIC_FLAGS_T *atomic_flags, uint32_t flags); ++ ++/** ++ * Retrieve the current flags and then clear them. The entire operation is ++ * atomic. ++ * ++ * @param atomic_flags Instance to get/clear flags from/on ++ * ++ * @return Mask of flags which were set (and we cleared) ++ */ ++VCOS_INLINE_DECL ++uint32_t vcos_atomic_flags_get_and_clear(VCOS_ATOMIC_FLAGS_T *atomic_flags); ++ ++/** ++ * Delete an atomic flags instance. ++ * ++ * @param atomic_flags Instance to delete ++ */ ++VCOS_INLINE_DECL ++void vcos_atomic_flags_delete(VCOS_ATOMIC_FLAGS_T *atomic_flags); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_build_info.h +@@ -0,0 +1,5 @@ ++const char *vcos_get_build_hostname( void ); ++const char *vcos_get_build_version( void ); ++const char *vcos_get_build_time( void ); ++const char *vcos_get_build_date( void ); ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_cfg.h +@@ -0,0 +1,113 @@ ++/***************************************************************************** ++* Copyright 2009 - 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#if !defined( VCOS_CFG_H ) ++#define VCOS_CFG_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++typedef struct opaque_vcos_cfg_buf_t *VCOS_CFG_BUF_T; ++typedef struct opaque_vcos_cfg_entry_t *VCOS_CFG_ENTRY_T; ++ ++/** \file vcos_file.h ++ * ++ * API for accessing configuration/statistics information. This ++ * is loosely modelled on the linux proc entries. ++ */ ++ ++typedef void (*VCOS_CFG_SHOW_FPTR)( VCOS_CFG_BUF_T buf, void *data ); ++typedef void (*VCOS_CFG_PARSE_FPTR)( VCOS_CFG_BUF_T buf, void *data ); ++ ++/** Create a configuration directory. ++ * ++ * @param entry Place to store the created config entry. ++ * @param parent Parent entry (for directory like config ++ * options). ++ * @param entryName Name of the directory. ++ */ ++ ++VCOS_STATUS_T vcos_cfg_mkdir( VCOS_CFG_ENTRY_T *entry, ++ VCOS_CFG_ENTRY_T *parent, ++ const char *dirName ); ++ ++/** Create a configuration entry. ++ * ++ * @param entry Place to store the created config entry. ++ * @param parent Parent entry (for directory like config ++ * options). ++ * @param entryName Name of the configuration entry. ++ * @param showFunc Function pointer to show configuration ++ * data. ++ * @param parseFunc Function pointer to parse new data. ++ */ ++ ++VCOS_STATUS_T vcos_cfg_create_entry( VCOS_CFG_ENTRY_T *entry, ++ VCOS_CFG_ENTRY_T *parent, ++ const char *entryName, ++ VCOS_CFG_SHOW_FPTR showFunc, ++ VCOS_CFG_PARSE_FPTR parseFunc, ++ void *data ); ++ ++/** Determines if a configuration entry has been created or not. ++ * ++ * @param entry Configuration entry to query. ++ */ ++ ++int vcos_cfg_is_entry_created( VCOS_CFG_ENTRY_T entry ); ++ ++/** Returns the name of a configuration entry. ++ * ++ * @param entry Configuration entry to query. ++ */ ++ ++const char *vcos_cfg_get_entry_name( VCOS_CFG_ENTRY_T entry ); ++ ++/** Removes a configuration entry. ++ * ++ * @param entry Configuration entry to remove. ++ */ ++ ++VCOS_STATUS_T vcos_cfg_remove_entry( VCOS_CFG_ENTRY_T *entry ); ++ ++ ++/** Writes data into a configuration buffer. Only valid inside ++ * the show function. ++ * ++ * @param buf Buffer to write data into. ++ * @param fmt printf style format string. ++ */ ++ ++void vcos_cfg_buf_printf( VCOS_CFG_BUF_T buf, const char *fmt, ... ); ++ ++/** Retrieves a null terminated string of the data associated ++ * with the buffer. Only valid inside the parse function. ++ * ++ * @param buf Buffer to get data from. ++ * @param fmt printf style format string. ++ */ ++ ++char *vcos_cfg_buf_get_str( VCOS_CFG_BUF_T buf ); ++ ++void *vcos_cfg_get_proc_entry( VCOS_CFG_ENTRY_T entry ); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_cmd.h +@@ -0,0 +1,98 @@ ++/***************************************************************************** ++* Copyright 2009 - 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#if !defined( VCOS_CMD_H ) ++#define VCOS_CMD_H ++ ++/* ---- Include Files ----------------------------------------------------- */ ++ ++#include "interface/vcos/vcos.h" ++#include "interface/vcos/vcos_stdint.h" ++ ++ ++/* ---- Constants and Types ---------------------------------------------- */ ++ ++struct VCOS_CMD_S; ++typedef struct VCOS_CMD_S VCOS_CMD_T; ++ ++typedef struct ++{ ++ int argc; /* Number of arguments (includes the command/sub-command) */ ++ char **argv; /* Array of arguments */ ++ char **argv_orig; /* Original array of arguments */ ++ ++ VCOS_CMD_T *cmd_entry; ++ VCOS_CMD_T *cmd_parent_entry; ++ ++ int use_log; /* Output being logged? */ ++ size_t result_size; /* Size of result buffer. */ ++ char *result_ptr; /* Next place to put output. */ ++ char *result_buf; /* Start of the buffer. */ ++ ++} VCOS_CMD_PARAM_T; ++ ++typedef VCOS_STATUS_T (*VCOS_CMD_FUNC_T)( VCOS_CMD_PARAM_T *param ); ++ ++struct VCOS_CMD_S ++{ ++ const char *name; ++ const char *args; ++ VCOS_CMD_FUNC_T cmd_fn; ++ VCOS_CMD_T *sub_cmd_entry; ++ const char *descr; ++ ++}; ++ ++/* ---- Variable Externs ------------------------------------------------- */ ++ ++/* ---- Function Prototypes ---------------------------------------------- */ ++ ++/* ++ * Common printing routine for generating command output. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_cmd_error( VCOS_CMD_PARAM_T *param, const char *fmt, ... ) VCOS_FORMAT_ATTR_(printf, 2, 3); ++VCOSPRE_ void VCOSPOST_ vcos_cmd_printf( VCOS_CMD_PARAM_T *param, const char *fmt, ... ) VCOS_FORMAT_ATTR_(printf, 2, 3); ++VCOSPRE_ void VCOSPOST_ vcos_cmd_vprintf( VCOS_CMD_PARAM_T *param, const char *fmt, va_list args ) VCOS_FORMAT_ATTR_(printf, 2, 0); ++ ++/* ++ * Cause vcos_cmd_error, printf and vprintf to always log to the provided ++ * category. When this call is made, the results buffer passed into ++ * vcos_cmd_execute is used as a line buffer and does not need to be ++ * output by the caller. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_cmd_always_log_output( VCOS_LOG_CAT_T *log_category ); ++ ++/* ++ * Prints command usage for the current command. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_cmd_usage( VCOS_CMD_PARAM_T *param ); ++ ++/* ++ * Register commands to be processed ++ */ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_cmd_register( VCOS_CMD_T *cmd_entry ); ++ ++/* ++ * Registers multiple commands to be processed. The array should ++ * be terminated by an entry with all zeros. ++ */ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_cmd_register_multiple( VCOS_CMD_T *cmd_entry ); ++ ++/* ++ * Executes a command based on a command line. ++ */ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_cmd_execute( int argc, char **argv, size_t result_size, char *result_buf ); ++ ++#endif /* VCOS_CMD_H */ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_ctype.h +@@ -0,0 +1,29 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - public header file ++=============================================================================*/ ++ ++#ifndef VCOS_CTYPE_H ++#define VCOS_CTYPE_H ++ ++/** ++ * \file ++ * ++ * ctype functions. ++ * ++ */ ++ ++#ifdef __KERNEL__ ++#include <linux/ctype.h> ++#else ++#include <ctype.h> ++#endif ++ ++#endif ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_dlfcn.h +@@ -0,0 +1,69 @@ ++/*============================================================================= ++Copyright (c) 2010 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VCOS - abstraction over dynamic library opening ++=============================================================================*/ ++ ++#ifndef VCOS_DLFCN_H ++#define VCOS_DLFCN_H ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#define VCOS_DL_LAZY 1 ++#define VCOS_DL_NOW 2 ++ ++/** ++ * \file ++ * ++ * Loading dynamic libraries. See also dlfcn.h. ++ */ ++ ++/** Open a dynamic library. ++ * ++ * @param name name of the library ++ * @param mode Load lazily or immediately (VCOS_DL_LAZY, VCOS_DL_NOW). ++ * ++ * @return A handle for use in subsequent calls. ++ */ ++VCOSPRE_ void * VCOSPOST_ vcos_dlopen(const char *name, int mode); ++ ++/** Look up a symbol. ++ * ++ * @param handle Handle to open ++ * @param name Name of function ++ * ++ * @return Function pointer, or NULL. ++ */ ++VCOSPRE_ void VCOSPOST_ (*vcos_dlsym(void *handle, const char *name))(void); ++ ++/** Close a library ++ * ++ * @param handle Handle to close ++ */ ++VCOSPRE_ int VCOSPOST_ vcos_dlclose (void *handle); ++ ++/** Return error message from library. ++ * ++ * @param err On return, set to non-zero if an error has occurred ++ * @param buf Buffer to write error to ++ * @param len Size of buffer (including terminating NUL). ++ */ ++VCOSPRE_ int VCOSPOST_ vcos_dlerror(int *err, char *buf, size_t buflen); ++ ++ ++#ifdef __cplusplus ++} ++#endif ++#endif ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_event.h +@@ -0,0 +1,97 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - public header file for events ++=============================================================================*/ ++ ++#ifndef VCOS_EVENT_H ++#define VCOS_EVENT_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++/** ++ * \file ++ * ++ * An event is akin to the Win32 auto-reset event. ++ * ++ * ++ * Signalling an event will wake up one waiting thread only. Once one ++ * thread has been woken the event atomically returns to the unsignalled ++ * state. ++ * ++ * If no threads are waiting on the event when it is signalled it remains ++ * signalled. ++ * ++ * This is almost, but not quite, completely unlike the "event flags" ++ * object based on Nucleus event groups and ThreadX event flags. ++ * ++ * In particular, it should be similar in speed to a semaphore, unlike ++ * the event flags. ++ */ ++ ++/** ++ * Create an event instance. ++ * ++ * @param event Filled in with constructed event. ++ * @param name Name of the event (for debugging) ++ * ++ * @return VCOS_SUCCESS on success, or error code. ++ */ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_event_create(VCOS_EVENT_T *event, const char *name); ++ ++#ifndef vcos_event_signal ++ ++/** ++ * Signal the event. The event will return to being unsignalled ++ * after exactly one waiting thread has been woken up. If no ++ * threads are waiting it remains signalled. ++ * ++ * @param event The event to signal ++ */ ++VCOS_INLINE_DECL ++void vcos_event_signal(VCOS_EVENT_T *event); ++ ++/** ++ * Wait for the event. ++ * ++ * @param event The event to wait for ++ * @return VCOS_SUCCESS on success, VCOS_EAGAIN if the wait was interrupted. ++ */ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_event_wait(VCOS_EVENT_T *event); ++ ++/** ++ * Try event, but don't block. ++ * ++ * @param event The event to try ++ * @return VCOS_SUCCESS on success, VCOS_EAGAIN if the event is not currently signalled ++ */ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_event_try(VCOS_EVENT_T *event); ++ ++#endif ++ ++/* ++ * Destroy an event. ++ */ ++VCOS_INLINE_DECL ++void vcos_event_delete(VCOS_EVENT_T *event); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_event_flags.h +@@ -0,0 +1,98 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - public header file ++=============================================================================*/ ++ ++#ifndef VCOS_EVENT_FLAGS_H ++#define VCOS_EVENT_FLAGS_H ++ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++#define VCOS_EVENT_FLAGS_SUSPEND VCOS_SUSPEND ++#define VCOS_EVENT_FLAGS_NO_SUSPEND VCOS_NO_SUSPEND ++typedef VCOS_OPTION VCOS_EVENTGROUP_OPERATION_T; ++ ++/** ++ * \file vcos_event_flags.h ++ * ++ * Defines event flags API. ++ * ++ * Similar to Nucleus event groups. ++ * ++ * These have the same semantics as Nucleus event groups and ThreadX event ++ * flags. As such, they are quite complex internally; if speed is important ++ * they might not be your best choice. ++ * ++ */ ++ ++/** ++ * Create an event flags instance. ++ * ++ * @param flags Pointer to event flags instance, filled in on return. ++ * @param name Name for the event flags, used for debug. ++ * ++ * @return VCOS_SUCCESS if succeeded. ++ */ ++ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_event_flags_create(VCOS_EVENT_FLAGS_T *flags, const char *name); ++ ++/** ++ * Set some events. ++ * ++ * @param flags Instance to set flags on ++ * @param events Bitmask of the flags to actually set ++ * @param op How the flags should be set. VCOS_OR will OR in the flags; VCOS_AND ++ * will AND them in, possibly clearing existing flags. ++ */ ++VCOS_INLINE_DECL ++void vcos_event_flags_set(VCOS_EVENT_FLAGS_T *flags, ++ VCOS_UNSIGNED events, ++ VCOS_OPTION op); ++ ++/** ++ * Retrieve some events. ++ * ++ * Waits until the specified events have been set. ++ * ++ * @param flags Instance to wait on ++ * @param requested_events The bitmask to wait for ++ * @param op VCOS_OR - get any; VCOS_AND - get all. ++ * @param ms_suspend How long to wait, in milliseconds ++ * @param retrieved_events the events actually retrieved. ++ * ++ * @return VCOS_SUCCESS if events were retrieved. VCOS_EAGAIN if the ++ * timeout expired. ++ */ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_event_flags_get(VCOS_EVENT_FLAGS_T *flags, ++ VCOS_UNSIGNED requested_events, ++ VCOS_OPTION op, ++ VCOS_UNSIGNED ms_suspend, ++ VCOS_UNSIGNED *retrieved_events); ++ ++ ++/** ++ * Delete an event flags instance. ++ */ ++VCOS_INLINE_DECL ++void vcos_event_flags_delete(VCOS_EVENT_FLAGS_T *); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_init.h +@@ -0,0 +1,43 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - initialization routines ++=============================================================================*/ ++ ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** \file ++ * ++ * Some OS support libraries need some initialization. To support this, call this ++ * function at the start of day. ++ */ ++ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_init(void); ++VCOSPRE_ void VCOSPOST_ vcos_deinit(void); ++VCOSPRE_ void VCOSPOST_ vcos_global_lock(void); ++VCOSPRE_ void VCOSPOST_ vcos_global_unlock(void); ++ ++/** Pass in the argv/argc arguments passed to main() */ ++VCOSPRE_ void VCOSPOST_ vcos_set_args(int argc, const char **argv); ++ ++/** Return argc. */ ++VCOSPRE_ int VCOSPOST_ vcos_get_argc(void); ++ ++/** Return argv. */ ++VCOSPRE_ const char ** VCOSPOST_ vcos_get_argv(void); ++ ++#ifdef __cplusplus ++} ++#endif ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_logging.h +@@ -0,0 +1,279 @@ ++/*============================================================================= ++Copyright (c) 2009-2011 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - logging support ++=============================================================================*/ ++ ++#ifndef VCOS_LOGGING_H ++#define VCOS_LOGGING_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include <stdarg.h> ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++/** ++ * \file ++ * ++ * Logging support ++ * ++ * This provides categorised logging. Clients register ++ * a category, and then get a number of logging levels for ++ * that category. ++ * ++ * The logging level flag is tested using a flag *before* the ++ * function call, which makes logging very fast when disabled - there ++ * is no function call overhead just to find out that this log ++ * message is disabled. ++ * ++ * \section VCOS_LOG_CATEGORY ++ * ++ * As a convenience, clients define VCOS_LOG_CATEGORY to point to ++ * their category; the various vcos_log_xxx() macros then expand to ++ * use this. ++ * ++ * e.g. ++ * ++ * #define VCOS_LOG_CATEGORY (&my_category) ++ * ++ * #include <interface/vcos/vcos.h> ++ * ++ * VCOS_LOG_CAT_T my_category; ++ * ++ * .... ++ * ++ * vcos_log_trace("Stuff happened: %d", n_stuff); ++ * ++ */ ++ ++/** Logging levels */ ++typedef enum VCOS_LOG_LEVEL_T ++{ ++ VCOS_LOG_UNINITIALIZED = 0, ++ VCOS_LOG_NEVER, ++ VCOS_LOG_ERROR, ++ VCOS_LOG_WARN, ++ VCOS_LOG_INFO, ++ VCOS_LOG_TRACE, ++} VCOS_LOG_LEVEL_T; ++ ++ ++/** Initialize a logging category without going through vcos_log_register(). ++ * ++ * This is useful for the case where there is no obvious point to do the ++ * registration (no initialization function for the module). However, it ++ * means that your logging category is not registered, so cannot be easily ++ * changed at run-time. ++ */ ++#define VCOS_LOG_INIT(n,l) { l, n, 0, {0}, 0, 0 } ++ ++/** A registered logging category. ++ */ ++typedef struct VCOS_LOG_CAT_T ++{ ++ VCOS_LOG_LEVEL_T level; /** Which levels are enabled for this category */ ++ const char *name; /** Name for this category. */ ++ struct VCOS_LOG_CAT_T *next; ++ struct { ++ unsigned int want_prefix:1; ++ } flags; ++ unsigned int refcount; ++ void *platform_data; /** platform specific data */ ++} VCOS_LOG_CAT_T; ++ ++typedef void (*VCOS_VLOG_IMPL_FUNC_T)(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args); ++ ++/** Convert a VCOS_LOG_LEVEL_T into a printable string. ++ * The platform needs to implement this function. ++ */ ++VCOSPRE_ const char * VCOSPOST_ vcos_log_level_to_string( VCOS_LOG_LEVEL_T level ); ++ ++/** Convert a string into a VCOS_LOG_LEVEL_T ++ * The platform needs to implement this function. ++ */ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_string_to_log_level( const char *str, VCOS_LOG_LEVEL_T *level ); ++ ++/** Log a message. Basic API. Normal code should not use this. ++ * The platform needs to implement this function. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_log_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, ...) VCOS_FORMAT_ATTR_(printf, 3, 4); ++ ++/** Log a message using a varargs parameter list. Normal code should ++ * not use this. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_vlog_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args) VCOS_FORMAT_ATTR_(printf, 3, 0); ++ ++/** Set the function which does the actual logging output. ++ * Passing in NULL causes the default logging function to be ++ * used. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_set_vlog_impl( VCOS_VLOG_IMPL_FUNC_T vlog_impl_func ); ++ ++/** The default logging function, which is provided by each ++ * platform. ++ */ ++ ++VCOSPRE_ void VCOSPOST_ vcos_vlog_default_impl(const VCOS_LOG_CAT_T *cat, VCOS_LOG_LEVEL_T _level, const char *fmt, va_list args) VCOS_FORMAT_ATTR_(printf, 3, 0); ++ ++/* ++ * Initialise the logging subsystem. This is called from ++ * vcos_init() so you don't normally need to call it. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_logging_init(void); ++ ++/** Register a logging category. ++ * ++ * @param name the name of this category. ++ * @param category the category to register. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_log_register(const char *name, VCOS_LOG_CAT_T *category); ++ ++/** Unregister a logging category. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_log_unregister(VCOS_LOG_CAT_T *category); ++ ++/** Return a default logging category, for people too lazy to create their own. ++ * ++ * Using the default category will be slow (there's an extra function ++ * call overhead). Don't do this in normal code. ++ */ ++VCOSPRE_ const VCOS_LOG_CAT_T * VCOSPOST_ vcos_log_get_default_category(void); ++ ++VCOSPRE_ void VCOSPOST_ vcos_set_log_options(const char *opt); ++ ++/** Set the logging level for a category at run time. Without this, the level ++ * will be that set by vcos_log_register from a platform-specific source. ++ * ++ * @param category the category to modify. ++ * @param level the new logging level for this category. ++ */ ++VCOS_STATIC_INLINE void vcos_log_set_level(VCOS_LOG_CAT_T *category, VCOS_LOG_LEVEL_T level) ++{ ++ category->level = level; ++} ++ ++#define vcos_log_dump_mem(cat,label,addr,voidMem,numBytes) do { if (vcos_is_log_enabled(cat,VCOS_LOG_TRACE)) vcos_log_dump_mem_impl(cat,label,addr,voidMem,numBytes); } while (0) ++ ++void vcos_log_dump_mem_impl( const VCOS_LOG_CAT_T *cat, ++ const char *label, ++ uint32_t addr, ++ const void *voidMem, ++ size_t numBytes ); ++ ++/* ++ * Platform specific hooks (optional). ++ */ ++#ifndef vcos_log_platform_init ++#define vcos_log_platform_init() (void)0 ++#endif ++ ++#ifndef vcos_log_platform_register ++#define vcos_log_platform_register(category) (void)0 ++#endif ++ ++#ifndef vcos_log_platform_unregister ++#define vcos_log_platform_unregister(category) (void)0 ++#endif ++ ++/* VCOS_TRACE() - deprecated macro which just outputs in a debug build and ++ * is a no-op in a release build. ++ * ++ * _VCOS_LOG_X() - internal macro which outputs if the current level for the ++ * particular category is higher than the supplied message level. ++ */ ++ ++#define VCOS_LOG_DFLT_CATEGORY vcos_log_get_default_category() ++ ++#define _VCOS_LEVEL(x) (x) ++ ++#define vcos_is_log_enabled(cat,_level) (_VCOS_LEVEL((cat)->level) >= _VCOS_LEVEL(_level)) ++ ++#if defined(_VCOS_METAWARE) || defined(__GNUC__) ++ ++# if !defined(NDEBUG) || defined(VCOS_ALWAYS_WANT_LOGGING) ++# define VCOS_LOGGING_ENABLED ++# define _VCOS_LOG_X(cat, _level, fmt...) do { if (vcos_is_log_enabled(cat,_level)) vcos_log_impl(cat,_level,fmt); } while (0) ++# define _VCOS_VLOG_X(cat, _level, fmt, ap) do { if (vcos_is_log_enabled(cat,_level)) vcos_vlog_impl(cat,_level,fmt,ap); } while (0) ++# else ++# define _VCOS_LOG_X(cat, _level, fmt...) (void)0 ++# define _VCOS_VLOG_X(cat, _level, fmt, ap) (void)0 ++# endif ++ ++ ++ ++# define vcos_log_error(...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_ERROR, __VA_ARGS__) ++# define vcos_log_warn(...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_WARN, __VA_ARGS__) ++# define vcos_log_info(...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_INFO, __VA_ARGS__) ++# define vcos_log_trace(...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_TRACE, __VA_ARGS__) ++ ++# define vcos_vlog_error(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_ERROR, fmt, ap) ++# define vcos_vlog_warn(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_WARN, fmt, ap) ++# define vcos_vlog_info(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_INFO, fmt, ap) ++# define vcos_vlog_trace(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_TRACE, fmt, ap) ++ ++# define vcos_log(...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, __VA_ARGS__) ++# define vcos_vlog(fmt,ap) _VCOS_VLOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, fmt, ap) ++# define VCOS_ALERT(...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_ERROR, __VA_ARGS__) ++# define VCOS_TRACE(...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, __VA_ARGS__) ++ ++/* ++ * MS Visual Studio - pre 2005 does not grok variadic macros ++ */ ++#elif defined(_MSC_VER) ++ ++# if _MSC_VER >= 1400 ++ ++# if !defined(NDEBUG) || defined(VCOS_ALWAYS_WANT_LOGGING) ++# define VCOS_LOGGING_ENABLED ++# define _VCOS_LOG_X(cat, _level, fmt,...) do { if (vcos_is_log_enabled(cat,_level)) vcos_log_impl(cat, _level, fmt, __VA_ARGS__); } while (0) ++# else ++# define _VCOS_LOG_X(cat, _level, fmt,...) (void)0 ++# endif ++ ++# define vcos_log_error(fmt,...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_ERROR, fmt, __VA_ARGS__) ++# define vcos_log_warn(fmt,...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_WARN, fmt, __VA_ARGS__) ++# define vcos_log_info(fmt,...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_INFO, fmt, __VA_ARGS__) ++# define vcos_log_trace(fmt,...) _VCOS_LOG_X(VCOS_LOG_CATEGORY, VCOS_LOG_TRACE, fmt, __VA_ARGS__) ++ ++# define vcos_log(fmt,...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, fmt) ++# define VCOS_ALERT(fmt,...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_ERROR, fmt) ++# define VCOS_TRACE(fmt,...) _VCOS_LOG_X(VCOS_LOG_DFLT_CATEGORY, VCOS_LOG_INFO, fmt) ++ ++# else /* _MSC_VER >= 1400 */ ++ ++/* do not define these */ ++ ++# endif /* _MSC_VER >= 1400 */ ++ ++#endif ++ ++#if VCOS_HAVE_CMD ++ ++#include "interface/vcos/vcos_cmd.h" ++ ++/* ++ * These are the log sub-commands. They're exported here for user-mode apps which ++ * may want to call these, since the "log" command isn't registered for user-mode ++ * apps (vcdbg for example, has its own log command). ++ */ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_log_assert_cmd( VCOS_CMD_PARAM_T *param ); ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_log_set_cmd( VCOS_CMD_PARAM_T *param ); ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_log_status_cmd( VCOS_CMD_PARAM_T *param ); ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_log_test_cmd( VCOS_CMD_PARAM_T *param ); ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++#endif /* VCOS_LOGGING_H */ ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_lowlevel_thread.h +@@ -0,0 +1,107 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - low level thread support ++=============================================================================*/ ++ ++#ifndef VCOS_LOWLEVEL_THREAD_H ++#define VCOS_LOWLEVEL_THREAD_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++/** ++ * \file ++ * ++ * This defines a low level thread API that is supported by *some* operating systems ++ * and can be used to construct the regular "joinable thread" API on those operating ++ * systems. ++ * ++ * Most clients will not need to use this code. ++ * ++ * \sa vcos_joinable_thread.h ++ */ ++ ++/** ++ * \brief Create a thread. ++ * ++ * This creates a thread which can be stopped either by returning from the ++ * entry point function or by calling vcos_llthread_exit from within the entry ++ * point function. The thread must be cleaned up by calling ++ * vcos_llthread_delete. vcos_llthread_delete may or may not terminate the ++ * thread. ++ * ++ * The preemptible parameter familiar from Nucleus is removed, as it is unused in ++ * VideoCore code. Affinity is added, since we do use this. ++ * ++ * @param thread Filled in with thread instance ++ * @param name An optional name for the thread. "" may be used (but ++ * a name will aid in debugging). ++ * @param entry Entry point ++ * @param arg A single argument passed to the entry point function ++ * @param stack Pointer to stack address ++ * @param stacksz Size of stack in bytes ++ * @param priority Priority of task, between VCOS_PRI_LOW and VCOS_PRI_HIGH ++ * @param affinity CPU affinity ++ * ++ * @sa vcos_llthread_terminate vcos_llthread_delete ++ */ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_llthread_create(VCOS_LLTHREAD_T *thread, ++ const char *name, ++ VCOS_LLTHREAD_ENTRY_FN_T entry, ++ void *arg, ++ void *stack, ++ VCOS_UNSIGNED stacksz, ++ VCOS_UNSIGNED priority, ++ VCOS_UNSIGNED affinity, ++ VCOS_UNSIGNED timeslice, ++ VCOS_UNSIGNED autostart); ++ ++/** ++ * \brief Exits the current thread. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_llthread_exit(void); ++ ++/** ++ * \brief Delete a thread. This must be called to cleanup after ++ * vcos_llthread_create. This may or may not terminate the thread. ++ * It does not clean up any resources that may have been ++ * allocated by the thread. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_llthread_delete(VCOS_LLTHREAD_T *thread); ++ ++/** ++ * \brief Return current lowlevel thread pointer. ++ */ ++VCOS_INLINE_DECL ++VCOS_LLTHREAD_T *vcos_llthread_current(void); ++ ++/** ++ * Resume a thread. ++ */ ++VCOS_INLINE_DECL ++void vcos_llthread_resume(VCOS_LLTHREAD_T *thread); ++ ++VCOSPRE_ int VCOSPOST_ vcos_llthread_running(VCOS_LLTHREAD_T *thread); ++ ++/** ++ * \brief Create a VCOS_LLTHREAD_T for the current thread. This is so we can ++ * have VCOS_LLTHREAD_Ts even for threads not originally created by VCOS (eg ++ * the thread that calls vcos_init). ++ */ ++extern VCOS_STATUS_T _vcos_llthread_create_attach(VCOS_LLTHREAD_T *thread); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_mem.h +@@ -0,0 +1,81 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - memory support ++=============================================================================*/ ++ ++#ifndef VCOS_MEM_H ++#define VCOS_MEM_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++/** \file ++ * ++ * Memory allocation api (malloc/free equivalents) is for benefit of host ++ * applications. VideoCore code should use rtos_XXX functions. ++ * ++ */ ++ ++ ++/** Allocate memory ++ * ++ * @param size Size of memory to allocate ++ * @param description Description, to aid in debugging. May be ignored internally on some platforms. ++ */ ++VCOS_INLINE_DECL ++void *vcos_malloc(VCOS_UNSIGNED size, const char *description); ++ ++void *vcos_kmalloc(VCOS_UNSIGNED size, const char *description); ++void *vcos_kcalloc(VCOS_UNSIGNED num, VCOS_UNSIGNED size, const char *description); ++ ++/** Allocate cleared memory ++ * ++ * @param num Number of items to allocate. ++ * @param size Size of each item in bytes. ++ * @param description Description, to aid in debugging. May be ignored internally on some platforms. ++ */ ++VCOS_INLINE_DECL ++void *vcos_calloc(VCOS_UNSIGNED num, VCOS_UNSIGNED size, const char *description); ++ ++/** Free memory ++ * ++ * Free memory that has been allocated. ++ */ ++VCOS_INLINE_DECL ++void vcos_free(void *ptr); ++ ++void vcos_kfree(void *ptr); ++ ++/** Allocate aligned memory ++ * ++ * Allocate memory aligned on the specified boundary. ++ * ++ * @param size Size of memory to allocate ++ * @param description Description, to aid in debugging. May be ignored internally on some platforms. ++ */ ++VCOS_INLINE_DECL ++void *vcos_malloc_aligned(VCOS_UNSIGNED size, VCOS_UNSIGNED align, const char *description); ++ ++/** Return the amount of free heap memory ++ * ++ */ ++VCOS_INLINE_DECL ++unsigned long vcos_get_free_mem(void); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++#endif ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_msgqueue.h +@@ -0,0 +1,157 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VCOS - packet-like messages, based loosely on those found in TRIPOS. ++=============================================================================*/ ++ ++#ifndef VCOS_MSGQUEUE_H ++#define VCOS_MSGQUEUE_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++/** ++ * \file ++ * ++ * Packet-like messages, based loosely on those found in TRIPOS and ++ * derivatives thereof. ++ * ++ * A task can send a message *pointer* to another task, where it is ++ * queued on a linked list and the task woken up. The receiving task ++ * consumes all of the messages on its input queue, and optionally ++ * sends back replies using the original message memory. ++ * ++ * A caller can wait for the reply to a specific message - any other ++ * messages that arrive in the meantime are queued separately. ++ * ++ * ++ * All messages have a standard common layout, but the payload area can ++ * be used freely to extend this. ++ */ ++ ++/** Map the payload portion of a message to a structure pointer. ++ */ ++#define VCOS_MSG_DATA(_msg) (void*)((_msg)->data) ++ ++/** Standard message ids - FIXME - these need to be done properly! */ ++#define VCOS_MSG_N_QUIT 1 ++#define VCOS_MSG_N_OPEN 2 ++#define VCOS_MSG_N_CLOSE 3 ++#define VCOS_MSG_N_PRIVATE (1<<20) ++ ++#define VCOS_MSG_REPLY_BIT (1<<31) ++ ++/** Make gnuc compiler be happy about pointer punning */ ++#ifdef __GNUC__ ++#define __VCOS_MAY_ALIAS __attribute__((__may_alias__)) ++#else ++#define __VCOS_MAY_ALIAS ++#endif ++ ++/** A single message queue. ++ */ ++typedef struct VCOS_MSGQUEUE_T ++{ ++ struct VCOS_MSG_T *head; /**< head of linked list of messages waiting on this queue */ ++ struct VCOS_MSG_T *tail; /**< tail of message queue */ ++ VCOS_SEMAPHORE_T sem; /**< thread waits on this for new messages */ ++ VCOS_MUTEX_T lock; /**< locks the messages list */ ++} VCOS_MSGQUEUE_T; ++ ++/** A single message ++ */ ++typedef struct VCOS_MSG_T ++{ ++ uint32_t code; /**< message code */ ++ int error; /**< error status signalled back to caller */ ++ VCOS_MSGQUEUE_T *dst; /**< destination queue */ ++ VCOS_MSGQUEUE_T *src; /**< source; replies go back to here */ ++ struct VCOS_MSG_T *next; /**< next in queue */ ++ VCOS_THREAD_T *src_thread; /**< for debug */ ++ uint32_t data[25]; /**< payload area */ ++} VCOS_MSG_T; ++ ++/** An endpoint ++ */ ++typedef struct VCOS_MSG_ENDPOINT_T ++{ ++ VCOS_MSGQUEUE_T primary; /**< incoming messages */ ++ VCOS_MSGQUEUE_T secondary; /**< this is used for waitspecific */ ++ char name[32]; /**< name of this endpoint, for find() */ ++ struct VCOS_MSG_ENDPOINT_T *next; /**< next in global list of endpoints */ ++} VCOS_MSG_ENDPOINT_T; ++#define MSG_REPLY_BIT (1<<31) ++ ++/** Initalise the library. Normally called from vcos_init(). ++ */ ++extern VCOS_STATUS_T vcos_msgq_init(void); ++ ++/** Find a message queue by name and get a handle to it. ++ * ++ * @param name the name of the queue to find ++ * ++ * @return The message queue, or NULL if not found. ++ */ ++VCOSPRE_ VCOS_MSGQUEUE_T VCOSPOST_ *vcos_msgq_find(const char *name); ++ ++/** Wait for a message queue to come into existence. If it already exists, ++ * return immediately, otherwise block. ++ * ++ * On the whole, if you find yourself using this, it is probably a sign ++ * of poor design, since you should create all the server threads first, ++ * and then the client threads. But it is sometimes useful. ++ * ++ * @param name the name of the queue to find ++ * @return The message queue ++ */ ++VCOSPRE_ VCOS_MSGQUEUE_T VCOSPOST_ *vcos_msgq_wait(const char *name); ++ ++/** Send a message. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_msg_send(VCOS_MSGQUEUE_T *dest, uint32_t code, VCOS_MSG_T *msg); ++ ++/** Send a message and wait for a reply. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_msg_sendwait(VCOS_MSGQUEUE_T *queue, uint32_t code, VCOS_MSG_T *msg); ++ ++/** Wait for a message on this thread's endpoint. ++ */ ++VCOSPRE_ VCOS_MSG_T * VCOSPOST_ vcos_msg_wait(void); ++ ++/** Wait for a specific message. ++ */ ++VCOS_MSG_T * vcos_msg_wait_specific(VCOS_MSGQUEUE_T *queue, VCOS_MSG_T *msg); ++ ++/** Peek for a message on this thread's endpoint, if a message is not available, NULL is ++ returned. If a message is available it will be removed from the endpoint and returned. ++ */ ++VCOSPRE_ VCOS_MSG_T * VCOSPOST_ vcos_msg_peek(void); ++ ++/** Send a reply to a message ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_msg_reply(VCOS_MSG_T *msg); ++ ++/** Create an endpoint. Each thread should need no more than one of these - if you ++ * find yourself needing a second one, you've done something wrong. ++ */ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_msgq_endpoint_create(VCOS_MSG_ENDPOINT_T *ep, const char *name); ++ ++/** Destroy an endpoint. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_msgq_endpoint_delete(VCOS_MSG_ENDPOINT_T *ep); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_mutex.h +@@ -0,0 +1,92 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - mutex public header file ++=============================================================================*/ ++ ++#ifndef VCOS_MUTEX_H ++#define VCOS_MUTEX_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++/** ++ * \file vcos_mutex.h ++ * ++ * Mutex API. Mutexes are not re-entrant, as supporting this adds extra code ++ * that slows down clients which have been written sensibly. ++ * ++ * \sa vcos_reentrant_mutex.h ++ * ++ */ ++ ++/** Create a mutex. ++ * ++ * @param m Filled in with mutex on return ++ * @param name A non-null name for the mutex, used for diagnostics. ++ * ++ * @return VCOS_SUCCESS if mutex was created, or error code. ++ */ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_mutex_create(VCOS_MUTEX_T *m, const char *name); ++ ++/** Delete the mutex. ++ */ ++VCOS_INLINE_DECL ++void vcos_mutex_delete(VCOS_MUTEX_T *m); ++ ++/** ++ * \brief Wait to claim the mutex. ++ * ++ * On most platforms this always returns VCOS_SUCCESS, and so would ideally be ++ * a void function, however some platforms allow a wait to be interrupted so ++ * it remains non-void. ++ * ++ * Try to obtain the mutex. ++ * @param m Mutex to wait on ++ * @return VCOS_SUCCESS - mutex was taken. ++ * VCOS_EAGAIN - could not take mutex. ++ */ ++#ifndef vcos_mutex_lock ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_mutex_lock(VCOS_MUTEX_T *m); ++ ++/** Release the mutex. ++ */ ++VCOS_INLINE_DECL ++void vcos_mutex_unlock(VCOS_MUTEX_T *m); ++#endif ++ ++/** Test if the mutex is already locked. ++ * ++ * @return 1 if mutex is locked, 0 if it is unlocked. ++ */ ++VCOS_INLINE_DECL ++int vcos_mutex_is_locked(VCOS_MUTEX_T *m); ++ ++/** Obtain the mutex if possible. ++ * ++ * @param m the mutex to try to obtain ++ * ++ * @return VCOS_SUCCESS if mutex is succesfully obtained, or VCOS_EAGAIN ++ * if it is already in use by another thread. ++ */ ++#ifndef vcos_mutex_trylock ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_mutex_trylock(VCOS_MUTEX_T *m); ++#endif ++ ++ ++#ifdef __cplusplus ++} ++#endif ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_once.h +@@ -0,0 +1,42 @@ ++/*============================================================================= ++Copyright (c) 2011 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - 'once' ++=============================================================================*/ ++ ++#ifndef VCOS_ONCE_H ++#define VCOS_ONCE_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++/** ++ * \file vcos_once.h ++ * ++ * Ensure something is called only once. ++ * ++ * Initialize once_control to VCOS_ONCE_INIT. The first ++ * time this is called, the init_routine will be called. Thereafter ++ * it won't. ++ * ++ * \sa pthread_once() ++ * ++ */ ++ ++VCOS_STATUS_T vcos_once(VCOS_ONCE_T *once_control, ++ void (*init_routine)(void)); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_semaphore.h +@@ -0,0 +1,115 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - public header file ++=============================================================================*/ ++ ++#ifndef VCOS_SEMAPHORE_H ++#define VCOS_SEMAPHORE_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++/** ++ * \file vcos_semaphore.h ++ * ++ * \section sem Semaphores ++ * ++ * This provides counting semaphores. Semaphores are not re-entrant. On sensible ++ * operating systems a semaphore can always be posted but can only be taken in ++ * thread (not interrupt) context. Under Nucleus, a LISR cannot post a semaphore, ++ * although it would not be hard to lift this restriction. ++ * ++ * \subsection timeout Timeout ++ * ++ * On both Nucleus and ThreadX a semaphore can be taken with a timeout. This is ++ * not supported by VCOS because it makes the non-timeout code considerably more ++ * complicated (and hence slower). In the unlikely event that you need a timeout ++ * with a semaphore, and you cannot simply redesign your code to avoid it, use ++ * an event flag (vcos_event_flags.h). ++ * ++ * \subsection sem_nucleus Changes from Nucleus: ++ * ++ * Semaphores are always "FIFO" - i.e. sleeping threads are woken in FIFO order. That's ++ * because: ++ * \arg there's no support for NU_PRIORITY in threadx (though it can be emulated, slowly) ++ * \arg we don't appear to actually consciously use it - for example, Dispmanx uses ++ * it, but all threads waiting are the same priority. ++ * ++ */ ++ ++/** ++ * \brief Create a semaphore. ++ * ++ * Create a semaphore. ++ * ++ * @param sem Pointer to memory to be initialized ++ * @param name A name for this semaphore. The name may be truncated internally. ++ * @param count The initial count for the semaphore. ++ * ++ * @return VCOS_SUCCESS if the semaphore was created. ++ * ++ */ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_semaphore_create(VCOS_SEMAPHORE_T *sem, const char *name, VCOS_UNSIGNED count); ++ ++/** ++ * \brief Wait on a semaphore. ++ * ++ * There is no timeout option on a semaphore, as adding this will slow down ++ * implementations on some platforms. If you need that kind of behaviour, use ++ * an event group. ++ * ++ * On most platforms this always returns VCOS_SUCCESS, and so would ideally be ++ * a void function, however some platforms allow a wait to be interrupted so ++ * it remains non-void. ++ * ++ * @param sem Semaphore to wait on ++ * @return VCOS_SUCCESS - semaphore was taken. ++ * VCOS_EAGAIN - could not take semaphore ++ * ++ */ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_semaphore_wait(VCOS_SEMAPHORE_T *sem); ++ ++/** ++ * \brief Try to wait for a semaphore. ++ * ++ * Try to obtain the semaphore. If it is already taken, return VCOS_TIMEOUT. ++ * @param sem Semaphore to wait on ++ * @return VCOS_SUCCESS - semaphore was taken. ++ * VCOS_EAGAIN - could not take semaphore ++ */ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_semaphore_trywait(VCOS_SEMAPHORE_T *sem); ++ ++/** ++ * \brief Post a semaphore. ++ * ++ * @param sem Semaphore to wait on ++ */ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_semaphore_post(VCOS_SEMAPHORE_T *sem); ++ ++/** ++ * \brief Delete a semaphore, releasing any resources consumed by it. ++ * ++ * @param sem Semaphore to wait on ++ */ ++VCOS_INLINE_DECL ++void vcos_semaphore_delete(VCOS_SEMAPHORE_T *sem); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_stdbool.h +@@ -0,0 +1,17 @@ ++#ifndef VCOS_STDBOOL_H ++#define VCOS_STDBOOL_H ++ ++#ifndef __cplusplus ++ ++#if defined(__STDC__) && (__STDC_VERSION__ >= 199901L) ++#include <stdbool.h> ++#else ++typedef enum { ++ false, ++ true ++} bool; ++#endif ++ ++#endif /* __cplusplus */ ++ ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_stdint.h +@@ -0,0 +1,193 @@ ++/*============================================================================= ++Copyright (c) 2011 Broadcom Europe Limited. ++All rights reserved. ++ ++FILE DESCRIPTION ++ ++=============================================================================*/ ++ ++#ifndef VCOS_STDINT_H ++#define VCOS_STDINT_H ++ ++/* Attempt to provide the types defined in stdint.h. ++ * ++ * Ideally this would either call out to a platform-specific ++ * header file (e.g. stdint.h) or define the types on a ++ * per-architecture/compiler basis. But for now we just ++ * use #ifdefs. ++ */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#ifdef __SYMBIAN32__ ++ ++typedef signed char int8_t; ++typedef unsigned char uint8_t; ++ ++typedef signed short int16_t; ++typedef unsigned short uint16_t; ++ ++typedef int16_t int_least16_t; ++ ++typedef signed long int32_t; ++typedef unsigned long uint32_t; ++ ++typedef signed long long int64_t; ++typedef unsigned long long uint64_t; ++ ++typedef int32_t intptr_t; ++typedef uint32_t uintptr_t; ++ ++typedef int64_t intmax_t; ++typedef uint64_t uintmax_t; ++ ++#define INT8_MIN SCHAR_MIN ++#define INT8_MAX SCHAR_MAX ++#define UINT8_MAX UCHAR_MAX ++#define INT16_MIN SHRT_MIN ++#define INT16_MAX SHRT_MAX ++#define UINT16_MAX USHRT_MAX ++#define INT32_MIN LONG_MIN ++#define INT32_MAX LONG_MAX ++#define UINT32_MAX ULONG_MAX ++#define INT64_MIN LLONG_MIN ++#define INT64_MAX LLONG_MAX ++#define UINT64_MAX ULLONG_MAX ++ ++#define INTPTR_MIN INT32_MIN ++#define INTPTR_MAX INT32_MAX ++#define UINTPTR_MAX UINT32_MAX ++#define INTMAX_MIN INT64_MIN ++#define INTMAX_MAX INT64_MAX ++#define INT_LEAST16_MAX INT16_MAX ++#define INT_LEAST16_MAX INT16_MAX ++ ++/*{{{ C99 types - THIS WHOLE SECTION IS INCOMPATIBLE WITH C99. IT SHOULD RESIDE IN A STDINT.H SINCE THIS FILE GETS USED ON HOST SIDE */ ++ ++#elif defined( __STDC__ ) && __STDC_VERSION__ >= 199901L ++ ++#include <stdint.h> ++ ++#elif defined( __GNUC__ ) ++ ++#include <stdint.h> ++ ++#elif defined(_MSC_VER) /* Visual C define equivalent types */ ++ ++#include <stddef.h> /* Avoids intptr_t being defined in vadefs.h */ ++ ++typedef __int8 int8_t; ++typedef unsigned __int8 uint8_t; ++ ++typedef __int16 int16_t; ++typedef unsigned __int16 uint16_t; ++ ++typedef __int32 int32_t; ++typedef unsigned __int32 uint32_t; ++ ++typedef __int64 int64_t; ++typedef unsigned __int64 uint64_t; ++typedef uint32_t uintptr_t; ++typedef int64_t intmax_t; ++typedef uint64_t uintmax_t; ++typedef int16_t int_least16_t; ++ ++#elif defined (VCMODS_LCC) ++#include <limits.h> ++ ++typedef signed char int8_t; ++typedef unsigned char uint8_t; ++ ++typedef signed short int16_t; ++typedef unsigned short uint16_t; ++ ++typedef signed long int32_t; ++typedef unsigned long uint32_t; ++ ++typedef signed long int64_t; /*!!!! PFCD, this means code using 64bit numbers will be broken on the VCE */ ++typedef unsigned long uint64_t; /* !!!! PFCD */ ++ ++typedef int32_t intptr_t; ++typedef uint32_t uintptr_t; ++typedef int64_t intmax_t; ++typedef uint64_t uintmax_t; ++typedef int16_t int_least16_t; ++ ++#define INT8_MIN SCHAR_MIN ++#define INT8_MAX SCHAR_MAX ++#define UINT8_MAX UCHAR_MAX ++#define INT16_MIN SHRT_MIN ++#define INT16_MAX SHRT_MAX ++#define UINT16_MAX USHRT_MAX ++#define INT32_MIN LONG_MIN ++#define INT32_MAX LONG_MAX ++#define UINT32_MAX ULONG_MAX ++#define INT64_MIN LONG_MIN /* !!!! PFCD */ ++#define INT64_MAX LONG_MAX /* !!!! PFCD */ ++#define UINT64_MAX ULONG_MAX /* !!!! PFCD */ ++ ++#define INTPTR_MIN INT32_MIN ++#define INTPTR_MAX INT32_MAX ++#define UINTPTR_MAX UINT32_MAX ++#define INTMAX_MIN INT64_MIN ++#define INTMAX_MIN INT64_MIN ++#define INT_LEAST16_MAX INT16_MAX ++#define INT_LEAST16_MAX INT16_MAX ++ ++#elif defined(__VIDEOCORE__) ++ ++typedef signed char int8_t; ++typedef unsigned char uint8_t; ++ ++typedef signed short int16_t; ++typedef unsigned short uint16_t; ++ ++typedef signed long int32_t; ++typedef unsigned long uint32_t; ++ ++typedef signed long long int64_t; ++typedef unsigned long long uint64_t; ++ ++typedef int32_t intptr_t; ++typedef uint32_t uintptr_t; ++typedef int64_t intmax_t; ++typedef uint64_t uintmax_t; ++typedef int16_t int_least16_t; ++ ++#define INT8_MIN SCHAR_MIN ++#define INT8_MAX SCHAR_MAX ++#define UINT8_MAX UCHAR_MAX ++#define INT16_MIN SHRT_MIN ++#define INT16_MAX SHRT_MAX ++#define UINT16_MAX USHRT_MAX ++#define INT32_MIN LONG_MIN ++#define INT32_MAX LONG_MAX ++#define UINT32_MAX ULONG_MAX ++#define INT64_MIN LLONG_MIN ++#define INT64_MAX LLONG_MAX ++#define UINT64_MAX ULLONG_MAX ++ ++#define INTPTR_MIN INT32_MIN ++#define INTPTR_MAX INT32_MAX ++#define UINTPTR_MAX UINT32_MAX ++#define INTMAX_MIN INT64_MIN ++#define INTMAX_MAX INT64_MAX ++#define INT_LEAST16_MAX INT16_MAX ++#define INT_LEAST16_MAX INT16_MAX ++ ++#elif defined (__HIGHC__) && defined(_I386) ++ ++#include <stdint.h> ++ ++#else ++#error Unknown platform ++#endif ++ ++#ifdef __cplusplus ++} ++#endif ++#endif /* VCOS_STDINT_H */ ++ ++ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_string.h +@@ -0,0 +1,73 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - public header file ++=============================================================================*/ ++ ++#ifndef VCOS_STRING_H ++#define VCOS_STRING_H ++ ++/** ++ * \file ++ * ++ * String functions. ++ * ++ */ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++#ifdef __KERNEL__ ++#include <linux/string.h> ++#else ++#include <string.h> ++#endif ++ ++/** Case insensitive string comparison. ++ * ++ */ ++ ++VCOS_INLINE_DECL ++int vcos_strcasecmp(const char *s1, const char *s2); ++ ++VCOS_INLINE_DECL ++int vcos_strncasecmp(const char *s1, const char *s2, size_t n); ++ ++VCOSPRE_ int VCOSPOST_ vcos_vsnprintf( char *buf, size_t buflen, const char *fmt, va_list ap ); ++ ++VCOSPRE_ int VCOSPOST_ vcos_snprintf(char *buf, size_t buflen, const char *fmt, ...); ++ ++VCOS_STATIC_INLINE ++int vcos_strlen(const char *s) { return (int)strlen(s); } ++ ++VCOS_STATIC_INLINE ++int vcos_strcmp(const char *s1, const char *s2) { return strcmp(s1,s2); } ++ ++VCOS_STATIC_INLINE ++int vcos_strncmp(const char *cs, const char *ct, size_t count) { return strncmp(cs, ct, count); } ++ ++VCOS_STATIC_INLINE ++char *vcos_strcpy(char *dst, const char *src) { return strcpy(dst, src); } ++ ++VCOS_STATIC_INLINE ++char *vcos_strncpy(char *dst, const char *src, size_t count) { return strncpy(dst, src, count); } ++ ++VCOS_STATIC_INLINE ++void *vcos_memcpy(void *dst, const void *src, size_t n) { memcpy(dst, src, n); return dst; } ++ ++VCOS_STATIC_INLINE ++void *vcos_memset(void *p, int c, size_t n) { return memset(p, c, n); } ++ ++#ifdef __cplusplus ++} ++#endif ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_thread.h +@@ -0,0 +1,259 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - public header file ++=============================================================================*/ ++ ++#ifndef VCOS_THREAD_H ++#define VCOS_THREAD_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++/** ++ * \file vcos_thread.h ++ * ++ * \section thread Threads ++ * ++ * Under Nucleus, a thread is created by NU_Create_Task, passing in the stack ++ * and various other parameters. To stop the thread, NU_Terminate_Thread() and ++ * NU_Delete_Thread() are called. ++ * ++ * Unfortunately it's not possible to emulate this API under some fairly common ++ * operating systems. Under Windows you can't pass in the stack, and you can't ++ * safely terminate a thread. ++ * ++ * Therefore, an API which is similar to the pthreads API is used instead. This ++ * API can (mostly) be emulated under all interesting operating systems. ++ * ++ * Obviously this makes the code somewhat more complicated on VideoCore than it ++ * would otherwise be - we end up with an extra mutex per thread, and some code ++ * that waits for it. The benefit is that we have a single way of creating ++ * threads that works consistently on all platforms (apart from stack supplying). ++ * ++ * \subsection stack Stack ++ * ++ * It's still not possible to pass in the stack address, but this can be made ++ * much more obvious in the API: the relevant function is missing and the ++ * CPP symbol VCOS_CAN_SET_STACK_ADDR is zero rather than one. ++ * ++ * \subsection thr_create Creating a thread ++ * ++ * The simplest way to create a thread is with vcos_thread_create() passing in a ++ * NULL thread parameter argument. To wait for the thread to exit, call ++ * vcos_thread_join(). ++ * ++ * \subsection back Backward compatibility ++ * ++ * To ease migration, a "classic" thread creation API is provided for code ++ * that used to make use of Nucleus, vcos_thread_create_classic(). The ++ * arguments are not exactly the same, as the PREEMPT parameter is dropped. ++ * ++ */ ++ ++#define VCOS_AFFINITY_CPU0 _VCOS_AFFINITY_CPU0 ++#define VCOS_AFFINITY_CPU1 _VCOS_AFFINITY_CPU1 ++#define VCOS_AFFINITY_MASK _VCOS_AFFINITY_MASK ++#define VCOS_AFFINITY_DEFAULT _VCOS_AFFINITY_DEFAULT ++#define VCOS_AFFINITY_THISCPU _VCOS_AFFINITY_THISCPU ++ ++/** Report whether or not we have an RTOS at all, and hence the ability to ++ * create threads. ++ */ ++VCOSPRE_ int VCOSPOST_ vcos_have_rtos(void); ++ ++/** Create a thread. It must be cleaned up by calling vcos_thread_join(). ++ * ++ * @param thread Filled in on return with thread ++ * @param name A name for the thread. May be the empty string. ++ * @param attrs Attributes; default attributes will be used if this is NULL. ++ * @param entry Entry point. ++ * @param arg Argument passed to the entry point. ++ */ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_thread_create(VCOS_THREAD_T *thread, ++ const char *name, ++ VCOS_THREAD_ATTR_T *attrs, ++ VCOS_THREAD_ENTRY_FN_T entry, ++ void *arg); ++ ++/** Exit the thread from within the thread function itself. ++ * Resources must still be cleaned up via a call to thread_join(). ++ * ++ * The thread can also be terminated by simply exiting the thread function. ++ * ++ * @param data Data passed to thread_join. May be NULL. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_thread_exit(void *data); ++ ++/** Wait for a thread to terminate and then clean up its resources. ++ * ++ * @param thread Thread to wait for ++ * @param pData Updated to point at data provided in vcos_thread_exit or exit ++ * code of thread function. ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_thread_join(VCOS_THREAD_T *thread, ++ void **pData); ++ ++ ++/** ++ * \brief Create a thread using an API similar to the one "traditionally" ++ * used under Nucleus. ++ * ++ * This creates a thread which must be cleaned up by calling vcos_thread_join(). ++ * The thread cannot be simply terminated (as in Nucleus and ThreadX) as thread ++ * termination is not universally supported. ++ * ++ * @param thread Filled in with thread instance ++ * @param name An optional name for the thread. NULL or "" may be used (but ++ * a name will aid in debugging). ++ * @param entry Entry point ++ * @param arg A single argument passed to the entry point function ++ * @param stack Pointer to stack address ++ * @param stacksz Size of stack in bytes ++ * @param priaff Priority of task, between VCOS_PRI_LOW and VCOS_PRI_HIGH, ORed with the CPU affinity ++ * @param autostart If non-zero the thread will start immediately. ++ * @param timeslice Timeslice (system ticks) for this thread. ++ * ++ * @sa vcos_thread_terminate vcos_thread_delete ++ */ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_thread_create_classic(VCOS_THREAD_T *thread, ++ const char *name, ++ void *(*entry)(void *arg), ++ void *arg, ++ void *stack, ++ VCOS_UNSIGNED stacksz, ++ VCOS_UNSIGNED priaff, ++ VCOS_UNSIGNED timeslice, ++ VCOS_UNSIGNED autostart); ++ ++/** ++ * \brief Set a thread's priority ++ * ++ * Set the priority for a thread. ++ * ++ * @param thread The thread ++ * @param pri Thread priority in VCOS_PRI_MASK bits; affinity in VCOS_AFFINITY_MASK bits. ++ */ ++VCOS_INLINE_DECL ++void vcos_thread_set_priority(VCOS_THREAD_T *thread, VCOS_UNSIGNED pri); ++ ++/** ++ * \brief Return the currently executing thread. ++ * ++ */ ++VCOS_INLINE_DECL ++VCOS_THREAD_T *vcos_thread_current(void); ++ ++/** ++ * \brief Return the thread's priority. ++ */ ++VCOS_INLINE_DECL ++VCOS_UNSIGNED vcos_thread_get_priority(VCOS_THREAD_T *thread); ++ ++/** ++ * \brief Return the thread's cpu affinity. ++ */ ++VCOS_INLINE_DECL ++VCOS_UNSIGNED vcos_thread_get_affinity(VCOS_THREAD_T *thread); ++ ++/** ++ * \brief Set the thread's cpu affinity. ++ */ ++ ++VCOS_INLINE_DECL ++void vcos_thread_set_affinity(VCOS_THREAD_T *thread, VCOS_UNSIGNED affinity); ++ ++/** ++ * \brief Query whether we are in an interrupt. ++ * ++ * @return 1 if in interrupt context. ++ */ ++VCOS_INLINE_DECL ++int vcos_in_interrupt(void); ++ ++/** ++ * \brief Sleep a while. ++ * ++ * @param ms Number of milliseconds to sleep for ++ * ++ * This may actually sleep a whole number of ticks. ++ */ ++VCOS_INLINE_DECL ++void vcos_sleep(uint32_t ms); ++ ++/** ++ * \brief Return the value of the hardware microsecond counter. ++ * ++ */ ++VCOS_INLINE_DECL ++uint32_t vcos_getmicrosecs(void); ++ ++#define vcos_get_ms() (vcos_getmicrosecs()/1000) ++ ++/** ++ * \brief Return a unique identifier for the current process ++ * ++ */ ++VCOS_INLINE_DECL ++VCOS_UNSIGNED vcos_process_id_current(void); ++ ++/** Relinquish this time slice. */ ++VCOS_INLINE_DECL ++void vcos_thread_relinquish(void); ++ ++/** Return the name of the given thread. ++ */ ++VCOSPRE_ const char * VCOSPOST_ vcos_thread_get_name(const VCOS_THREAD_T *thread); ++ ++/** Change preemption. This is almost certainly not what you want, as it won't ++ * work reliably in a multicore system: although you can affect the preemption ++ * on *this* core, you won't affect what's happening on the other core(s). ++ * ++ * It's mainly here to ease migration. If you're using it in new code, you ++ * probably need to think again. ++ * ++ * @param pe New preemption, VCOS_PREEMPT or VCOS_NO_PREEMPT ++ * @return Old value of preemption. ++ */ ++VCOS_INLINE_DECL ++VCOS_UNSIGNED vcos_change_preemption(VCOS_UNSIGNED pe); ++ ++/** Is a thread still running, or has it exited? ++ * ++ * Note: this exists for some fairly scary code in the video codec tests. Don't ++ * try to use it for anything else, as it may well not do what you expect. ++ * ++ * @param thread thread to query ++ * @return non-zero if thread is running, or zero if it has exited. ++ */ ++VCOS_INLINE_DECL ++int vcos_thread_running(VCOS_THREAD_T *thread); ++ ++/** Resume a thread. ++ * ++ * @param thread thread to resume ++ */ ++VCOS_INLINE_DECL ++void vcos_thread_resume(VCOS_THREAD_T *thread); ++ ++/* ++ * Internal APIs - may not always be present and should not be used in ++ * client code. ++ */ ++ ++extern void _vcos_task_timer_set(void (*pfn)(void*), void *, VCOS_UNSIGNED ms); ++extern void _vcos_task_timer_cancel(void); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_thread_attr.h +@@ -0,0 +1,73 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - thread attributes ++=============================================================================*/ ++ ++#ifndef VCOS_THREAD_ATTR_H ++#define VCOS_THREAD_ATTR_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++/** ++ * \file ++ * ++ * Attributes for thread creation. ++ * ++ */ ++ ++/** Initialize thread attribute struct. This call does not allocate memory, ++ * and so cannot fail. ++ * ++ */ ++VCOSPRE_ void VCOSPOST_ vcos_thread_attr_init(VCOS_THREAD_ATTR_T *attrs); ++ ++/** Set the stack address and size. If not set, a stack will be allocated automatically. ++ * ++ * This can only be set on some platforms. It will always be possible to set the stack ++ * address on VideoCore, but on host platforms, support may well not be available. ++ */ ++#if VCOS_CAN_SET_STACK_ADDR ++VCOS_INLINE_DECL ++void vcos_thread_attr_setstack(VCOS_THREAD_ATTR_T *attrs, void *addr, VCOS_UNSIGNED sz); ++#endif ++ ++/** Set the stack size. If not set, a default size will be used. Attempting to call this after having ++ * set the stack location with vcos_thread_attr_setstack() will result in undefined behaviour. ++ */ ++VCOS_INLINE_DECL ++void vcos_thread_attr_setstacksize(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED sz); ++ ++/** Set the task priority. If not set, a default value will be used. ++ */ ++VCOS_INLINE_DECL ++void vcos_thread_attr_setpriority(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED pri); ++ ++/** Set the task cpu affinity. If not set, the default will be used. ++ */ ++VCOS_INLINE_DECL ++void vcos_thread_attr_setaffinity(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED aff); ++ ++/** Set the timeslice. If not set the default will be used. ++ */ ++VCOS_INLINE_DECL ++void vcos_thread_attr_settimeslice(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED ts); ++ ++/** The thread entry function takes (argc,argv), as per Nucleus, with ++ * argc being 0. This may be withdrawn in a future release and should not ++ * be used in new code. ++ */ ++VCOS_INLINE_DECL ++void _vcos_thread_attr_setlegacyapi(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED legacy); ++ ++VCOS_INLINE_DECL ++void vcos_thread_attr_setautostart(VCOS_THREAD_ATTR_T *attrs, VCOS_UNSIGNED autostart); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_timer.h +@@ -0,0 +1,95 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++Project : vcfw ++Module : chip driver ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - timer support ++=============================================================================*/ ++ ++#ifndef VCOS_TIMER_H ++#define VCOS_TIMER_H ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++#include "interface/vcos/vcos_types.h" ++#include "vcos_platform.h" ++ ++/** \file vcos_timer.h ++ * ++ * Timers are single shot. ++ * ++ * Timer times are in milliseconds. ++ * ++ * \note that timer callback functions are called from an arbitrary thread ++ * context. The expiration function should do its work as quickly as possible; ++ * blocking should be avoided. ++ * ++ * \note On Windows, the separate function vcos_timer_init() must be called ++ * as timer initialization from DllMain is not possible. ++ */ ++ ++/** Perform timer subsystem initialization. This function is not needed ++ * on non-Windows platforms but is still present so that it can be ++ * called. On Windows it is needed because vcos_init() gets called ++ * from DLL initialization where it is not possible to create a ++ * time queue (deadlock occurs if you try). ++ * ++ * @return VCOS_SUCCESS on success. VCOS_EEXIST if this has already been called ++ * once. VCOS_ENOMEM if resource allocation failed. ++ */ ++VCOSPRE_ VCOS_STATUS_T VCOSPOST_ vcos_timer_init(void); ++ ++/** Create a timer in a disabled state. ++ * ++ * The timer is initially disabled. ++ * ++ * @param timer timer handle ++ * @param name name for timer ++ * @param expiration_routine function to call when timer expires ++ * @param context context passed to expiration routine ++ * ++ */ ++VCOS_INLINE_DECL ++VCOS_STATUS_T vcos_timer_create(VCOS_TIMER_T *timer, ++ const char *name, ++ void (*expiration_routine)(void *context), ++ void *context); ++ ++ ++ ++/** Start a timer running. ++ * ++ * Timer must be stopped. ++ * ++ * @param timer timer handle ++ * @param delay Delay to wait for, in ms ++ */ ++VCOS_INLINE_DECL ++void vcos_timer_set(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay); ++ ++/** Stop an already running timer. ++ * ++ * @param timer timer handle ++ */ ++VCOS_INLINE_DECL ++void vcos_timer_cancel(VCOS_TIMER_T *timer); ++ ++/** Stop a timer and restart it. ++ * @param timer timer handle ++ * @param delay delay in ms ++ */ ++VCOS_INLINE_DECL ++void vcos_timer_reset(VCOS_TIMER_T *timer, VCOS_UNSIGNED delay); ++ ++VCOS_INLINE_DECL ++void vcos_timer_delete(VCOS_TIMER_T *timer); ++ ++#ifdef __cplusplus ++} ++#endif ++#endif +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_types.h +@@ -0,0 +1,197 @@ ++/*============================================================================= ++Copyright (c) 2009 Broadcom Europe Limited. ++All rights reserved. ++ ++FILE DESCRIPTION ++VideoCore OS Abstraction Layer - basic types ++=============================================================================*/ ++ ++#ifndef VCOS_TYPES_H ++#define VCOS_TYPES_H ++ ++#define VCOS_VERSION 1 ++ ++#include "vcos_platform_types.h" ++ ++#if !defined(VCOSPRE_) || !defined(VCOSPOST_) ++#error VCOSPRE_ and VCOSPOST_ not defined! ++#endif ++ ++/* Redefine these here; this means that existing header files can carry on ++ * using the VCHPOST/VCHPRE macros rather than having huge changes, which ++ * could cause nasty merge problems. ++ */ ++#ifndef VCHPOST_ ++#define VCHPOST_ VCOSPOST_ ++#endif ++#ifndef VCHPRE_ ++#define VCHPRE_ VCOSPRE_ ++#endif ++ ++/** Entry function for a lowlevel thread. ++ * ++ * Returns void for consistency with Nucleus/ThreadX. ++ */ ++typedef void (*VCOS_LLTHREAD_ENTRY_FN_T)(void *); ++ ++/** Thread entry point. Returns a void* for consistency ++ * with pthreads. ++ */ ++typedef void *(*VCOS_THREAD_ENTRY_FN_T)(void*); ++ ++ ++/* Error return codes - chosen to be similar to errno values */ ++typedef enum ++{ ++ VCOS_SUCCESS, ++ VCOS_EAGAIN, ++ VCOS_ENOENT, ++ VCOS_ENOSPC, ++ VCOS_EINVAL, ++ VCOS_EACCESS, ++ VCOS_ENOMEM, ++ VCOS_ENOSYS, ++ VCOS_EEXIST, ++ VCOS_ENXIO, ++ VCOS_EINTR ++} VCOS_STATUS_T; ++ ++/* Some compilers (MetaWare) won't inline with -g turned on, which then results ++ * in a lot of code bloat. To overcome this, inline functions are forward declared ++ * with the prefix VCOS_INLINE_DECL, and implemented with the prefix VCOS_INLINE_IMPL. ++ * ++ * That then means that in a release build, "static inline" can be used in the obvious ++ * way, but in a debug build the implementations can be skipped in all but one file, ++ * by using VCOS_INLINE_BODIES. ++ * ++ * VCOS_INLINE_DECL - put this at the start of an inline forward declaration of a VCOS ++ * function. ++ * ++ * VCOS_INLINE_IMPL - put this at the start of an inlined implementation of a VCOS ++ * function. ++ * ++ */ ++ ++/* VCOS_EXPORT - it turns out that in some circumstances we need the implementation of ++ * a function even if it is usually inlined. ++ * ++ * In particular, if we have a codec that is usually provided in object form, if it ++ * was built for a debug build it will be full of calls to vcos_XXX(). If this is used ++ * in a *release* build, then there won't be any of these calls around in the main image ++ * as they will all have been inlined. The problem also exists for vcos functions called ++ * from assembler. ++ * ++ * VCOS_EXPORT ensures that the named function will be emitted as a regular (not static-inline) ++ * function inside vcos_<platform>.c so that it can be linked against. Doing this for every ++ * VCOS function would be a bit code-bloat-tastic, so it is only done for those that need it. ++ * ++ */ ++ ++#ifdef __cplusplus ++#define _VCOS_INLINE inline ++#else ++#define _VCOS_INLINE __inline ++#endif ++ ++#if defined(NDEBUG) ++ ++#ifdef __GNUC__ ++# define VCOS_INLINE_DECL extern __inline__ ++# define VCOS_INLINE_IMPL static __inline__ ++#else ++# define VCOS_INLINE_DECL static _VCOS_INLINE /* declare a func */ ++# define VCOS_INLINE_IMPL static _VCOS_INLINE /* implement a func inline */ ++#endif ++ ++# if defined(VCOS_WANT_IMPL) ++# define VCOS_EXPORT ++# else ++# define VCOS_EXPORT VCOS_INLINE_IMPL ++# endif /* VCOS_WANT_IMPL */ ++ ++#define VCOS_INLINE_BODIES ++ ++#else /* NDEBUG */ ++ ++#if !defined(VCOS_INLINE_DECL) ++ #define VCOS_INLINE_DECL extern ++#endif ++#if !defined(VCOS_INLINE_IMPL) ++ #define VCOS_INLINE_IMPL ++#endif ++#define VCOS_EXPORT VCOS_INLINE_IMPL ++#endif ++ ++#define VCOS_STATIC_INLINE static _VCOS_INLINE ++ ++#if defined(__HIGHC__) || defined(__HIGHC_ANSI__) ++#define _VCOS_METAWARE ++#endif ++ ++/** It seems that __FUNCTION__ isn't standard! ++ */ ++#if __STDC_VERSION__ < 199901L ++# if __GNUC__ >= 2 || defined(__VIDEOCORE__) ++# define VCOS_FUNCTION __FUNCTION__ ++# else ++# define VCOS_FUNCTION "<unknown>" ++# endif ++#else ++# define VCOS_FUNCTION __func__ ++#endif ++ ++#define _VCOS_MS_PER_TICK (1000/VCOS_TICKS_PER_SECOND) ++ ++/* Convert a number of milliseconds to a tick count. Internal use only - fails to ++ * convert VCOS_SUSPEND correctly. ++ */ ++#define _VCOS_MS_TO_TICKS(ms) (((ms)+_VCOS_MS_PER_TICK-1)/_VCOS_MS_PER_TICK) ++ ++#define VCOS_TICKS_TO_MS(ticks) ((ticks) * _VCOS_MS_PER_TICK) ++ ++/** VCOS version of DATESTR, from pcdisk.h. Used by the hostreq service. ++ */ ++typedef struct vcos_datestr ++{ ++ uint8_t cmsec; /**< Centesimal mili second */ ++ uint16_t date; /**< Date */ ++ uint16_t time; /**< Time */ ++ ++} VCOS_DATESTR; ++ ++/* Compile-time assert - declares invalid array length if condition ++ * not met, or array of length one if OK. ++ */ ++#define VCOS_CASSERT(e) extern char vcos_compile_time_check[1/(e)] ++ ++#define vcos_min(x,y) ((x) < (y) ? (x) : (y)) ++#define vcos_max(x,y) ((x) > (y) ? (x) : (y)) ++ ++/** Return the count of an array. FIXME: under gcc we could make ++ * this report an error for pointers using __builtin_types_compatible(). ++ */ ++#define vcos_countof(x) (sizeof((x)) / sizeof((x)[0])) ++ ++/* for backward compatibility */ ++#define countof(x) (sizeof((x)) / sizeof((x)[0])) ++ ++#define VCOS_ALIGN_DOWN(p,n) (((ptrdiff_t)(p)) & ~((n)-1)) ++#define VCOS_ALIGN_UP(p,n) VCOS_ALIGN_DOWN((ptrdiff_t)(p)+(n)-1,(n)) ++ ++/** bool_t is not a POSIX type so cannot rely on it. Define it here. ++ * It's not even defined in stdbool.h. ++ */ ++typedef int32_t vcos_bool_t; ++typedef int32_t vcos_fourcc_t; ++ ++#define VCOS_FALSE 0 ++#define VCOS_TRUE (!VCOS_FALSE) ++ ++/** Mark unused arguments to keep compilers quiet */ ++#define vcos_unused(x) (void)(x) ++ ++/** For backward compatibility */ ++typedef vcos_fourcc_t fourcc_t; ++typedef vcos_fourcc_t FOURCC_T; ++ ++#endif diff --git a/target/linux/brcm2708/patches-3.3/0006-Allow-mac-address-to-be-set-in-smsc95xx.patch b/target/linux/brcm2708/patches-3.3/0006-Allow-mac-address-to-be-set-in-smsc95xx.patch new file mode 100644 index 0000000000..cdcda3a068 --- /dev/null +++ b/target/linux/brcm2708/patches-3.3/0006-Allow-mac-address-to-be-set-in-smsc95xx.patch @@ -0,0 +1,96 @@ +From dd6079fc871e99e4b0345d3fe27701dce5dcc7d0 Mon Sep 17 00:00:00 2001 +From: popcornmix <popcornmix@gmail.com> +Date: Tue, 17 Jan 2012 19:22:59 +0000 +Subject: [PATCH 6/7] Allow mac address to be set in smsc95xx + +Signed-off-by: popcornmix <popcornmix@gmail.com> +--- + drivers/net/usb/smsc95xx.c | 56 ++++++++++++++++++++++++++++++++++++++++++++ + 1 files changed, 56 insertions(+), 0 deletions(-) + +diff --git a/drivers/net/usb/smsc95xx.c b/drivers/net/usb/smsc95xx.c +index f74f3ce..5a2e12b 100644 +--- a/drivers/net/usb/smsc95xx.c ++++ b/drivers/net/usb/smsc95xx.c +@@ -46,6 +46,7 @@ + #define SMSC95XX_INTERNAL_PHY_ID (1) + #define SMSC95XX_TX_OVERHEAD (8) + #define SMSC95XX_TX_OVERHEAD_CSUM (12) ++#define MAC_ADDR_LEN (6) + + struct smsc95xx_priv { + u32 mac_cr; +@@ -63,6 +64,10 @@ static int turbo_mode = true; + module_param(turbo_mode, bool, 0644); + MODULE_PARM_DESC(turbo_mode, "Enable multiple frames per Rx transaction"); + ++static char *macaddr = ":"; ++module_param(macaddr, charp, 0); ++MODULE_PARM_DESC(macaddr, "MAC address"); ++ + static int smsc95xx_read_reg(struct usbnet *dev, u32 index, u32 *data) + { + u32 *buf = kmalloc(4, GFP_KERNEL); +@@ -600,8 +605,59 @@ static int smsc95xx_ioctl(struct net_device *netdev, struct ifreq *rq, int cmd) + return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL); + } + ++/* Check the macaddr module parameter for a MAC address */ ++static int smsc95xx_is_macaddr_param(struct usbnet *dev, u8 *dev_mac) ++{ ++ int i, j, got_num, num; ++ u8 mtbl[MAC_ADDR_LEN]; ++ ++ if (macaddr[0] == ':') ++ return 0; ++ ++ i = 0; ++ j = 0; ++ num = 0; ++ got_num = 0; ++ while (j < MAC_ADDR_LEN) { ++ if (macaddr[i] && macaddr[i] != ':') { ++ got_num++; ++ if ('0' <= macaddr[i] && macaddr[i] <= '9') ++ num = num * 16 + macaddr[i] - '0'; ++ else if ('A' <= macaddr[i] && macaddr[i] <= 'F') ++ num = num * 16 + 10 + macaddr[i] - 'A'; ++ else if ('a' <= macaddr[i] && macaddr[i] <= 'f') ++ num = num * 16 + 10 + macaddr[i] - 'a'; ++ else ++ break; ++ i++; ++ } else if (got_num == 2) { ++ mtbl[j++] = (u8) num; ++ num = 0; ++ got_num = 0; ++ i++; ++ } else { ++ break; ++ } ++ } ++ ++ if (j == MAC_ADDR_LEN) { ++ netif_dbg(dev, ifup, dev->net, "Overriding MAC address with: " ++ "%02x:%02x:%02x:%02x:%02x:%02x\n", mtbl[0], mtbl[1], mtbl[2], ++ mtbl[3], mtbl[4], mtbl[5]); ++ for (i = 0; i < MAC_ADDR_LEN; i++) ++ dev_mac[i] = mtbl[i]; ++ return 1; ++ } else { ++ return 0; ++ } ++} ++ + static void smsc95xx_init_mac_address(struct usbnet *dev) + { ++ /* Check module parameters */ ++ if (smsc95xx_is_macaddr_param(dev, dev->net->dev_addr)) ++ return; ++ + /* try reading mac address from EEPROM */ + if (smsc95xx_read_eeprom(dev, EEPROM_MAC_OFFSET, ETH_ALEN, + dev->net->dev_addr) == 0) { +-- +1.7.5.4 + diff --git a/target/linux/brcm2708/patches-3.3/0007-Fix-headers-for-vchiq-vcos-to-be-GPLv2.patch b/target/linux/brcm2708/patches-3.3/0007-Fix-headers-for-vchiq-vcos-to-be-GPLv2.patch new file mode 100644 index 0000000000..cb187608df --- /dev/null +++ b/target/linux/brcm2708/patches-3.3/0007-Fix-headers-for-vchiq-vcos-to-be-GPLv2.patch @@ -0,0 +1,1493 @@ +From 9eaf792a2825b5b4d3b5c7f074354ed26e3f76ab Mon Sep 17 00:00:00 2001 +From: Dom Cobley <dc4@broadcom.com> +Date: Fri, 20 Jan 2012 18:23:29 +0000 +Subject: [PATCH 7/7] Fix headers for vchiq/vcos to be GPLv2 + +Signed-off-by: Dom Cobley <dc4@broadcom.com> +--- + .../misc/vc04_services/interface/vchi/vchi_mh.h | 26 +++++++++++++------- + .../interface/vcos/generic/vcos_common.h | 25 +++++++++++++----- + .../vcos/generic/vcos_generic_blockpool.h | 25 +++++++++++++----- + .../vcos/generic/vcos_generic_event_flags.c | 22 +++++++++++++--- + .../vcos/generic/vcos_generic_event_flags.h | 22 +++++++++++++--- + .../vcos/generic/vcos_generic_named_sem.h | 25 +++++++++++++----- + .../vcos/generic/vcos_generic_quickslow_mutex.h | 25 +++++++++++++----- + .../vcos/generic/vcos_generic_reentrant_mtx.h | 25 +++++++++++++----- + .../interface/vcos/generic/vcos_generic_tls.h | 25 +++++++++++++----- + .../vcos/generic/vcos_joinable_thread_from_plain.h | 24 +++++++++++++---- + .../interface/vcos/generic/vcos_latch_from_sem.h | 25 +++++++++++++----- + .../interface/vcos/generic/vcos_logcat.c | 25 +++++++++++++----- + .../interface/vcos/generic/vcos_mem_from_malloc.c | 25 +++++++++++++----- + .../interface/vcos/generic/vcos_mem_from_malloc.h | 25 +++++++++++++----- + .../vcos/generic/vcos_mutexes_are_reentrant.h | 25 +++++++++++++----- + .../interface/vcos/generic/vcos_thread_reaper.h | 25 +++++++++++++----- + .../interface/vcos/linuxkernel/stdint.h | 22 +++++++++++++--- + .../interface/vcos/linuxkernel/vcos_linuxkernel.c | 25 +++++++++++++----- + .../vcos/linuxkernel/vcos_linuxkernel_misc.c | 2 - + .../interface/vcos/linuxkernel/vcos_platform.h | 25 +++++++++++++----- + .../vcos/linuxkernel/vcos_platform_types.h | 25 +++++++++++++----- + drivers/misc/vc04_services/interface/vcos/vcos.h | 25 +++++++++++++----- + .../vc04_services/interface/vcos/vcos_assert.h | 25 +++++++++++++----- + .../interface/vcos/vcos_atomic_flags.h | 25 +++++++++++++----- + .../vc04_services/interface/vcos/vcos_build_info.h | 18 +++++++++++++ + .../misc/vc04_services/interface/vcos/vcos_ctype.h | 25 +++++++++++++----- + .../misc/vc04_services/interface/vcos/vcos_dlfcn.h | 25 +++++++++++++----- + .../misc/vc04_services/interface/vcos/vcos_event.h | 25 +++++++++++++----- + .../interface/vcos/vcos_event_flags.h | 25 +++++++++++++----- + .../misc/vc04_services/interface/vcos/vcos_init.h | 25 +++++++++++++----- + .../vc04_services/interface/vcos/vcos_logging.h | 25 +++++++++++++----- + .../interface/vcos/vcos_lowlevel_thread.h | 25 +++++++++++++----- + .../misc/vc04_services/interface/vcos/vcos_mem.h | 25 +++++++++++++----- + .../vc04_services/interface/vcos/vcos_msgqueue.h | 25 +++++++++++++----- + .../misc/vc04_services/interface/vcos/vcos_mutex.h | 25 +++++++++++++----- + .../misc/vc04_services/interface/vcos/vcos_once.h | 25 +++++++++++++----- + .../vc04_services/interface/vcos/vcos_semaphore.h | 25 +++++++++++++----- + .../vc04_services/interface/vcos/vcos_stdbool.h | 17 +++++++++++++ + .../vc04_services/interface/vcos/vcos_stdint.h | 24 +++++++++++++----- + .../vc04_services/interface/vcos/vcos_string.h | 25 +++++++++++++----- + .../vc04_services/interface/vcos/vcos_thread.h | 25 +++++++++++++----- + .../interface/vcos/vcos_thread_attr.h | 22 +++++++++++++--- + .../misc/vc04_services/interface/vcos/vcos_timer.h | 25 +++++++++++++----- + .../misc/vc04_services/interface/vcos/vcos_types.h | 22 +++++++++++++--- + 44 files changed, 771 insertions(+), 275 deletions(-) + +diff --git a/drivers/misc/vc04_services/interface/vchi/vchi_mh.h b/drivers/misc/vc04_services/interface/vchi/vchi_mh.h +index 01732bf..9bcf12e 100644 +--- a/drivers/misc/vc04_services/interface/vchi/vchi_mh.h ++++ b/drivers/misc/vc04_services/interface/vchi/vchi_mh.h +@@ -1,12 +1,20 @@ +-/*============================================================================= +-Copyright (c) 2010 Broadcom Europe Limited. All rights reserved. +- +-Project : vchi +-Module : vchi +- +-FILE DESCRIPTION: +-Definitions for memory handle types. +-=============================================================================*/ ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + + #ifndef VCHI_MH_H_ + #define VCHI_MH_H_ +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_common.h b/drivers/misc/vc04_services/interface/vcos/generic/vcos_common.h +index 2493122..ce7816957 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_common.h ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_common.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - common postamble code + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_blockpool.h b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_blockpool.h +index a048470..154b200 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_blockpool.h ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_blockpool.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2011 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - event flags implemented via a semaphore + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.c b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.c +index 8cd150f..3948a57 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.c ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.c +@@ -1,8 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - event flags implemented via mutexes + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.h b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.h +index 5205db7..8776ebe 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.h ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_event_flags.h +@@ -1,8 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - event flags implemented via a semaphore + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_named_sem.h b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_named_sem.h +index 15b332d..370562d 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_named_sem.h ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_named_sem.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - named semaphores + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_quickslow_mutex.h b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_quickslow_mutex.h +index e4ae649..bf7945c 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_quickslow_mutex.h ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_quickslow_mutex.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - reentrant mutexes created from regular ones. + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_reentrant_mtx.h b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_reentrant_mtx.h +index ffb0b27..27563ea 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_reentrant_mtx.h ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_reentrant_mtx.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - reentrant mutexes created from regular ones. + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_tls.h b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_tls.h +index 3af975f..22c059a 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_tls.h ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_generic_tls.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - generic thread local storage + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_joinable_thread_from_plain.h b/drivers/misc/vc04_services/interface/vcos/generic/vcos_joinable_thread_from_plain.h +index 5387b9e..fd0e198 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_joinable_thread_from_plain.h ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_joinable_thread_from_plain.h +@@ -1,10 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Module : vcos ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - implementation: joinable thread from plain + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_latch_from_sem.h b/drivers/misc/vc04_services/interface/vcos/generic/vcos_latch_from_sem.h +index 9ee5b4b..ec9e07b 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_latch_from_sem.h ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_latch_from_sem.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : vcos ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - Construct a latch from a semaphore + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_logcat.c b/drivers/misc/vc04_services/interface/vcos/generic/vcos_logcat.c +index d513fab..8b05179 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_logcat.c ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_logcat.c +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2010 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : vcos ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + Categorized logging for VCOS - a generic implementation. + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.c b/drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.c +index 7f22051..dd0574a 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.c ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.c +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : vcos ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - memory alloc implementation + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.h b/drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.h +index 1074dcc..8e2a18e 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.h ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_mem_from_malloc.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : VMCS Host Apps +-Module : Framework - VMCS ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + Create the vcos_malloc API from the regular system malloc/free + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_mutexes_are_reentrant.h b/drivers/misc/vc04_services/interface/vcos/generic/vcos_mutexes_are_reentrant.h +index d10352c9..a6a52b4 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_mutexes_are_reentrant.h ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_mutexes_are_reentrant.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - reentrant mutexes mapped directly to regular ones + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/generic/vcos_thread_reaper.h b/drivers/misc/vc04_services/interface/vcos/generic/vcos_thread_reaper.h +index a8fc3da..655dc25 100644 +--- a/drivers/misc/vc04_services/interface/vcos/generic/vcos_thread_reaper.h ++++ b/drivers/misc/vc04_services/interface/vcos/generic/vcos_thread_reaper.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2010 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : vcos ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - thread reaping + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/linuxkernel/stdint.h b/drivers/misc/vc04_services/interface/vcos/linuxkernel/stdint.h +index d3469af..1db1ecb 100644 +--- a/drivers/misc/vc04_services/interface/vcos/linuxkernel/stdint.h ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/stdint.h +@@ -1,8 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2010 Broadcom Europe Limited. +-All rights reserved. ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS fAbstraction Layer - stdint.h C standard header + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel.c b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel.c +index 154f4a1..0385540 100644 +--- a/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel.c ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel.c +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : vcos ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - pthreads types + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel_misc.c b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel_misc.c +index 4d934c3..4a9cedf 100644 +--- a/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel_misc.c ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_linuxkernel_misc.c +@@ -1,5 +1,3 @@ +-// ############################################################################# +-// START ####################################################################### + /***************************************************************************** + * Copyright 2009 - 2010 Broadcom Corporation. All rights reserved. + * +diff --git a/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform.h b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform.h +index 854001d..381688e 100644 +--- a/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform.h ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : vcos ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - Linux kernel (partial) implementation. + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform_types.h b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform_types.h +index 20d973e..f841e12 100644 +--- a/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform_types.h ++++ b/drivers/misc/vc04_services/interface/vcos/linuxkernel/vcos_platform_types.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : osal ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - platform-specific types and defines + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos.h b/drivers/misc/vc04_services/interface/vcos/vcos.h +index 4ff5cc8..e37d795 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - public header file + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_assert.h b/drivers/misc/vc04_services/interface/vcos/vcos_assert.h +index 5091621..1e72dff 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_assert.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_assert.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : osal ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - Assertion and error-handling macros. + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_atomic_flags.h b/drivers/misc/vc04_services/interface/vcos/vcos_atomic_flags.h +index bb8041e..317abf3 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_atomic_flags.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_atomic_flags.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver (just for consistency with the rest of vcos ;) ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - public header file + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_build_info.h b/drivers/misc/vc04_services/interface/vcos/vcos_build_info.h +index 7543a89..f3817f6 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_build_info.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_build_info.h +@@ -1,3 +1,21 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ + const char *vcos_get_build_hostname( void ); + const char *vcos_get_build_version( void ); + const char *vcos_get_build_time( void ); +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_ctype.h b/drivers/misc/vc04_services/interface/vcos/vcos_ctype.h +index 131b982..a270de8 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_ctype.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_ctype.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - public header file + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_dlfcn.h b/drivers/misc/vc04_services/interface/vcos/vcos_dlfcn.h +index 456b08e..0a683c0b 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_dlfcn.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_dlfcn.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2010 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VCOS - abstraction over dynamic library opening + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_event.h b/drivers/misc/vc04_services/interface/vcos/vcos_event.h +index f335059..38612f9 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_event.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_event.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - public header file for events + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_event_flags.h b/drivers/misc/vc04_services/interface/vcos/vcos_event_flags.h +index 6223c48..9eee410 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_event_flags.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_event_flags.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - public header file + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_init.h b/drivers/misc/vc04_services/interface/vcos/vcos_init.h +index e67327c..9fc5eca 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_init.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_init.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - initialization routines + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_logging.h b/drivers/misc/vc04_services/interface/vcos/vcos_logging.h +index 0c54781d..9702822 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_logging.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_logging.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009-2011 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - logging support + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_lowlevel_thread.h b/drivers/misc/vc04_services/interface/vcos/vcos_lowlevel_thread.h +index 658a30b..9473cec 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_lowlevel_thread.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_lowlevel_thread.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - low level thread support + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_mem.h b/drivers/misc/vc04_services/interface/vcos/vcos_mem.h +index 99860db5..af16208 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_mem.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_mem.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - memory support + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_msgqueue.h b/drivers/misc/vc04_services/interface/vcos/vcos_msgqueue.h +index 31f77c1..f0ef70b 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_msgqueue.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_msgqueue.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VCOS - packet-like messages, based loosely on those found in TRIPOS. + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_mutex.h b/drivers/misc/vc04_services/interface/vcos/vcos_mutex.h +index 5347ab2..14387a8 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_mutex.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_mutex.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - mutex public header file + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_once.h b/drivers/misc/vc04_services/interface/vcos/vcos_once.h +index d12ac5e..0533c10 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_once.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_once.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2011 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - 'once' + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_semaphore.h b/drivers/misc/vc04_services/interface/vcos/vcos_semaphore.h +index 5a32031..2760dd7 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_semaphore.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_semaphore.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - public header file + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_stdbool.h b/drivers/misc/vc04_services/interface/vcos/vcos_stdbool.h +index f1de3cb..3c7669d 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_stdbool.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_stdbool.h +@@ -1,3 +1,20 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + #ifndef VCOS_STDBOOL_H + #define VCOS_STDBOOL_H + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_stdint.h b/drivers/misc/vc04_services/interface/vcos/vcos_stdint.h +index 537ec33..c9a3e02 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_stdint.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_stdint.h +@@ -1,10 +1,20 @@ +-/*============================================================================= +-Copyright (c) 2011 Broadcom Europe Limited. +-All rights reserved. +- +-FILE DESCRIPTION +- +-=============================================================================*/ ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + + #ifndef VCOS_STDINT_H + #define VCOS_STDINT_H +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_string.h b/drivers/misc/vc04_services/interface/vcos/vcos_string.h +index 554b57d..c3d875f 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_string.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_string.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - public header file + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_thread.h b/drivers/misc/vc04_services/interface/vcos/vcos_thread.h +index 08a76a0..ee34648 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_thread.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_thread.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - public header file + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_thread_attr.h b/drivers/misc/vc04_services/interface/vcos/vcos_thread_attr.h +index 510991f..375dd01 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_thread_attr.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_thread_attr.h +@@ -1,8 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - thread attributes + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_timer.h b/drivers/misc/vc04_services/interface/vcos/vcos_timer.h +index 499adce..1612334 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_timer.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_timer.h +@@ -1,11 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. +- +-Project : vcfw +-Module : chip driver ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - timer support + =============================================================================*/ + +diff --git a/drivers/misc/vc04_services/interface/vcos/vcos_types.h b/drivers/misc/vc04_services/interface/vcos/vcos_types.h +index ab16f36..25de671 100644 +--- a/drivers/misc/vc04_services/interface/vcos/vcos_types.h ++++ b/drivers/misc/vc04_services/interface/vcos/vcos_types.h +@@ -1,8 +1,22 @@ +-/*============================================================================= +-Copyright (c) 2009 Broadcom Europe Limited. +-All rights reserved. ++/* ++ * Copyright (c) 2010-2011 Broadcom. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ + +-FILE DESCRIPTION ++/*============================================================================= + VideoCore OS Abstraction Layer - basic types + =============================================================================*/ + +-- +1.7.5.4 + diff --git a/target/linux/brcm2708/patches-3.3/0500-rpi-patches-geaf792a-9efb4705.patch b/target/linux/brcm2708/patches-3.3/0500-rpi-patches-geaf792a-9efb4705.patch new file mode 100644 index 0000000000..b5211d9caa --- /dev/null +++ b/target/linux/brcm2708/patches-3.3/0500-rpi-patches-geaf792a-9efb4705.patch @@ -0,0 +1,10488 @@ +--- a/arch/arm/configs/bcmrpi_cutdown_defconfig ++++ b/arch/arm/configs/bcmrpi_cutdown_defconfig +@@ -1,1307 +1,436 @@ +-# +-# Automatically generated file; DO NOT EDIT. +-# Linux/arm 3.1.9 Kernel Configuration +-# +-CONFIG_ARM=y +-CONFIG_SYS_SUPPORTS_APM_EMULATION=y +-CONFIG_GENERIC_GPIO=y +-# CONFIG_ARCH_USES_GETTIMEOFFSET is not set +-CONFIG_GENERIC_CLOCKEVENTS=y +-CONFIG_KTIME_SCALAR=y +-CONFIG_HAVE_PROC_CPU=y +-CONFIG_STACKTRACE_SUPPORT=y +-CONFIG_HAVE_LATENCYTOP_SUPPORT=y +-CONFIG_LOCKDEP_SUPPORT=y +-CONFIG_TRACE_IRQFLAGS_SUPPORT=y +-CONFIG_HARDIRQS_SW_RESEND=y +-CONFIG_GENERIC_IRQ_PROBE=y +-CONFIG_RWSEM_GENERIC_SPINLOCK=y +-CONFIG_ARCH_HAS_CPU_IDLE_WAIT=y +-CONFIG_GENERIC_HWEIGHT=y +-CONFIG_GENERIC_CALIBRATE_DELAY=y +-CONFIG_NEED_DMA_MAP_STATE=y +-CONFIG_VECTORS_BASE=0xffff0000 +-# CONFIG_ARM_PATCH_PHYS_VIRT is not set +-CONFIG_DEFCONFIG_LIST="/lib/modules/$UNAME_RELEASE/.config" +-CONFIG_HAVE_IRQ_WORK=y +- +-# +-# General setup +-# + CONFIG_EXPERIMENTAL=y +-CONFIG_BROKEN_ON_SMP=y +-CONFIG_INIT_ENV_ARG_LIMIT=32 +-CONFIG_CROSS_COMPILE="" +-CONFIG_LOCALVERSION="" + # CONFIG_LOCALVERSION_AUTO is not set +-CONFIG_HAVE_KERNEL_GZIP=y +-CONFIG_HAVE_KERNEL_LZMA=y +-CONFIG_HAVE_KERNEL_LZO=y +-CONFIG_KERNEL_GZIP=y +-# CONFIG_KERNEL_LZMA is not set +-# CONFIG_KERNEL_LZO is not set +-CONFIG_DEFAULT_HOSTNAME="(none)" +-CONFIG_SWAP=y + CONFIG_SYSVIPC=y +-CONFIG_SYSVIPC_SYSCTL=y + CONFIG_POSIX_MQUEUE=y +-CONFIG_POSIX_MQUEUE_SYSCTL=y +-# CONFIG_BSD_PROCESS_ACCT is not set +-# CONFIG_FHANDLE is not set +-# CONFIG_TASKSTATS is not set +-# CONFIG_AUDIT is not set +-CONFIG_HAVE_GENERIC_HARDIRQS=y +- +-# +-# IRQ subsystem +-# +-CONFIG_GENERIC_HARDIRQS=y +-CONFIG_HAVE_SPARSE_IRQ=y +-CONFIG_GENERIC_IRQ_SHOW=y +-# CONFIG_SPARSE_IRQ is not set +- +-# +-# RCU Subsystem +-# +-CONFIG_TINY_RCU=y +-# CONFIG_PREEMPT_RCU is not set +-# CONFIG_RCU_TRACE is not set +-# CONFIG_TREE_RCU_TRACE is not set + CONFIG_IKCONFIG=y + CONFIG_IKCONFIG_PROC=y +-CONFIG_LOG_BUF_SHIFT=17 +-# CONFIG_CGROUPS is not set +-# CONFIG_NAMESPACES is not set +-# CONFIG_SCHED_AUTOGROUP is not set +-# CONFIG_SYSFS_DEPRECATED is not set +-# CONFIG_RELAY is not set +-# CONFIG_BLK_DEV_INITRD is not set +-# CONFIG_CC_OPTIMIZE_FOR_SIZE is not set +-CONFIG_SYSCTL=y +-CONFIG_ANON_INODES=y +-CONFIG_EXPERT=y + # CONFIG_UID16 is not set +-CONFIG_SYSCTL_SYSCALL=y + # CONFIG_KALLSYMS is not set +-CONFIG_HOTPLUG=y +-CONFIG_PRINTK=y +-CONFIG_BUG=y +-# CONFIG_ELF_CORE is not set +-CONFIG_BASE_FULL=y +-CONFIG_FUTEX=y +-CONFIG_EPOLL=y +-CONFIG_SIGNALFD=y +-CONFIG_TIMERFD=y +-CONFIG_EVENTFD=y +-CONFIG_SHMEM=y +-CONFIG_AIO=y + CONFIG_EMBEDDED=y +-CONFIG_HAVE_PERF_EVENTS=y +-CONFIG_PERF_USE_VMALLOC=y +- +-# +-# Kernel Performance Events And Counters +-# +-# CONFIG_PERF_EVENTS is not set +-# CONFIG_PERF_COUNTERS is not set + # CONFIG_VM_EVENT_COUNTERS is not set + # CONFIG_COMPAT_BRK is not set + CONFIG_SLAB=y +-# CONFIG_SLUB is not set +-# CONFIG_SLOB is not set +-# CONFIG_PROFILING is not set +-CONFIG_HAVE_OPROFILE=y +-# CONFIG_KPROBES is not set +-CONFIG_HAVE_KPROBES=y +-CONFIG_HAVE_KRETPROBES=y +-CONFIG_HAVE_REGS_AND_STACK_ACCESS_API=y +-CONFIG_HAVE_CLK=y +-CONFIG_HAVE_DMA_API_DEBUG=y +- +-# +-# GCOV-based kernel profiling +-# +-CONFIG_HAVE_GENERIC_DMA_COHERENT=y +-CONFIG_SLABINFO=y +-CONFIG_RT_MUTEXES=y +-CONFIG_BASE_SMALL=0 + CONFIG_MODULES=y +-# CONFIG_MODULE_FORCE_LOAD is not set + CONFIG_MODULE_UNLOAD=y +-# CONFIG_MODULE_FORCE_UNLOAD is not set + CONFIG_MODVERSIONS=y + CONFIG_MODULE_SRCVERSION_ALL=y +-CONFIG_BLOCK=y +-CONFIG_LBDAF=y + # CONFIG_BLK_DEV_BSG is not set +-# CONFIG_BLK_DEV_BSGLIB is not set +-# CONFIG_BLK_DEV_INTEGRITY is not set +- +-# +-# IO Schedulers +-# +-CONFIG_IOSCHED_NOOP=y +-CONFIG_IOSCHED_DEADLINE=y +-CONFIG_IOSCHED_CFQ=y +-# CONFIG_DEFAULT_DEADLINE is not set +-CONFIG_DEFAULT_CFQ=y +-# CONFIG_DEFAULT_NOOP is not set +-CONFIG_DEFAULT_IOSCHED="cfq" +-# CONFIG_INLINE_SPIN_TRYLOCK is not set +-# CONFIG_INLINE_SPIN_TRYLOCK_BH is not set +-# CONFIG_INLINE_SPIN_LOCK is not set +-# CONFIG_INLINE_SPIN_LOCK_BH is not set +-# CONFIG_INLINE_SPIN_LOCK_IRQ is not set +-# CONFIG_INLINE_SPIN_LOCK_IRQSAVE is not set +-CONFIG_INLINE_SPIN_UNLOCK=y +-# CONFIG_INLINE_SPIN_UNLOCK_BH is not set +-CONFIG_INLINE_SPIN_UNLOCK_IRQ=y +-# CONFIG_INLINE_SPIN_UNLOCK_IRQRESTORE is not set +-# CONFIG_INLINE_READ_TRYLOCK is not set +-# CONFIG_INLINE_READ_LOCK is not set +-# CONFIG_INLINE_READ_LOCK_BH is not set +-# CONFIG_INLINE_READ_LOCK_IRQ is not set +-# CONFIG_INLINE_READ_LOCK_IRQSAVE is not set +-CONFIG_INLINE_READ_UNLOCK=y +-# CONFIG_INLINE_READ_UNLOCK_BH is not set +-CONFIG_INLINE_READ_UNLOCK_IRQ=y +-# CONFIG_INLINE_READ_UNLOCK_IRQRESTORE is not set +-# CONFIG_INLINE_WRITE_TRYLOCK is not set +-# CONFIG_INLINE_WRITE_LOCK is not set +-# CONFIG_INLINE_WRITE_LOCK_BH is not set +-# CONFIG_INLINE_WRITE_LOCK_IRQ is not set +-# CONFIG_INLINE_WRITE_LOCK_IRQSAVE is not set +-CONFIG_INLINE_WRITE_UNLOCK=y +-# CONFIG_INLINE_WRITE_UNLOCK_BH is not set +-CONFIG_INLINE_WRITE_UNLOCK_IRQ=y +-# CONFIG_INLINE_WRITE_UNLOCK_IRQRESTORE is not set +-# CONFIG_MUTEX_SPIN_ON_OWNER is not set +-CONFIG_FREEZER=y +- +-# +-# System Type +-# +-CONFIG_MMU=y +-# CONFIG_ARCH_INTEGRATOR is not set +-# CONFIG_ARCH_REALVIEW is not set +-# CONFIG_ARCH_VERSATILE is not set +-# CONFIG_ARCH_VEXPRESS is not set +-# CONFIG_ARCH_AT91 is not set +-# CONFIG_ARCH_BCMRING is not set +-# CONFIG_ARCH_CLPS711X is not set +-# CONFIG_ARCH_CNS3XXX is not set +-# CONFIG_ARCH_GEMINI is not set +-# CONFIG_ARCH_PRIMA2 is not set +-# CONFIG_ARCH_EBSA110 is not set +-# CONFIG_ARCH_EP93XX is not set +-# CONFIG_ARCH_FOOTBRIDGE is not set +-# CONFIG_ARCH_MXC is not set +-# CONFIG_ARCH_MXS is not set +-# CONFIG_ARCH_NETX is not set +-# CONFIG_ARCH_H720X is not set +-# CONFIG_ARCH_IOP13XX is not set +-# CONFIG_ARCH_IOP32X is not set +-# CONFIG_ARCH_IOP33X is not set +-# CONFIG_ARCH_IXP23XX is not set +-# CONFIG_ARCH_IXP2000 is not set +-# CONFIG_ARCH_IXP4XX is not set +-# CONFIG_ARCH_DOVE is not set +-# CONFIG_ARCH_KIRKWOOD is not set +-# CONFIG_ARCH_LPC32XX is not set +-# CONFIG_ARCH_MV78XX0 is not set +-# CONFIG_ARCH_ORION5X is not set +-# CONFIG_ARCH_MMP is not set +-# CONFIG_ARCH_KS8695 is not set +-# CONFIG_ARCH_W90X900 is not set +-# CONFIG_ARCH_NUC93X is not set +-# CONFIG_ARCH_TEGRA is not set +-# CONFIG_ARCH_PNX4008 is not set +-# CONFIG_ARCH_PXA is not set +-# CONFIG_ARCH_MSM is not set +-# CONFIG_ARCH_SHMOBILE is not set +-# CONFIG_ARCH_RPC is not set +-# CONFIG_ARCH_SA1100 is not set +-# CONFIG_ARCH_S3C2410 is not set +-# CONFIG_ARCH_S3C64XX is not set +-# CONFIG_ARCH_S5P64X0 is not set +-# CONFIG_ARCH_S5PC100 is not set +-# CONFIG_ARCH_S5PV210 is not set +-# CONFIG_ARCH_EXYNOS4 is not set +-# CONFIG_ARCH_SHARK is not set +-# CONFIG_ARCH_TCC_926 is not set +-# CONFIG_ARCH_U300 is not set +-# CONFIG_ARCH_U8500 is not set +-# CONFIG_ARCH_NOMADIK is not set +-# CONFIG_ARCH_DAVINCI is not set +-# CONFIG_ARCH_OMAP is not set +-# CONFIG_PLAT_SPEAR is not set + CONFIG_ARCH_BCM2708=y +-# CONFIG_ARCH_VT8500 is not set +-# CONFIG_ARCH_ZYNQ is not set +- +-# +-# System MMU +-# +- +-# +-# Broadcom BCM2708 Implementations +-# +-CONFIG_MACH_BCM2708=y +-CONFIG_BCM2708_GPIO=y +-CONFIG_BCM2708_VCMEM=y +- +-# +-# Processor Type +-# +-CONFIG_CPU_V6=y +-CONFIG_CPU_32v6=y +-CONFIG_CPU_ABRT_EV6=y +-CONFIG_CPU_PABRT_V6=y +-CONFIG_CPU_CACHE_V6=y +-CONFIG_CPU_CACHE_VIPT=y +-CONFIG_CPU_COPY_V6=y +-CONFIG_CPU_TLB_V6=y +-CONFIG_CPU_HAS_ASID=y +-CONFIG_CPU_CP15=y +-CONFIG_CPU_CP15_MMU=y +-CONFIG_CPU_USE_DOMAINS=y +- +-# +-# Processor Features +-# +-CONFIG_ARM_THUMB=y +-# CONFIG_CPU_ICACHE_DISABLE is not set +-# CONFIG_CPU_DCACHE_DISABLE is not set +-# CONFIG_CPU_BPREDICT_DISABLE is not set +-CONFIG_ARM_L1_CACHE_SHIFT=5 +-CONFIG_ARM_DMA_MEM_BUFFERABLE=y +-CONFIG_CPU_HAS_PMU=y +-CONFIG_ARM_ERRATA_411920=y +-# CONFIG_ARM_ERRATA_364296 is not set +- +-# +-# Bus support +-# +-CONFIG_ARM_AMBA=y +-# CONFIG_PCI_SYSCALL is not set +-# CONFIG_ARCH_SUPPORTS_MSI is not set +-# CONFIG_PCCARD is not set +- +-# +-# Kernel Features +-# +-CONFIG_TICK_ONESHOT=y + CONFIG_NO_HZ=y +-# CONFIG_HIGH_RES_TIMERS is not set +-CONFIG_GENERIC_CLOCKEVENTS_BUILD=y +-CONFIG_VMSPLIT_3G=y +-# CONFIG_VMSPLIT_2G is not set +-# CONFIG_VMSPLIT_1G is not set +-CONFIG_PAGE_OFFSET=0xC0000000 +-CONFIG_PREEMPT_NONE=y +-# CONFIG_PREEMPT_VOLUNTARY is not set +-# CONFIG_PREEMPT is not set +-CONFIG_HZ=100 ++CONFIG_HIGH_RES_TIMERS=y + CONFIG_AEABI=y +-CONFIG_OABI_COMPAT=y +-# CONFIG_ARCH_SPARSEMEM_DEFAULT is not set +-# CONFIG_ARCH_SELECT_MEMORY_MODEL is not set +-CONFIG_HAVE_ARCH_PFN_VALID=y +-# CONFIG_HIGHMEM is not set +-CONFIG_SELECT_MEMORY_MODEL=y +-CONFIG_FLATMEM_MANUAL=y +-CONFIG_FLATMEM=y +-CONFIG_FLAT_NODE_MEM_MAP=y +-CONFIG_HAVE_MEMBLOCK=y +-CONFIG_PAGEFLAGS_EXTENDED=y +-CONFIG_SPLIT_PTLOCK_CPUS=4 +-# CONFIG_COMPACTION is not set +-# CONFIG_PHYS_ADDR_T_64BIT is not set +-CONFIG_ZONE_DMA_FLAG=0 +-CONFIG_VIRT_TO_BUS=y +-# CONFIG_KSM is not set +-CONFIG_DEFAULT_MMAP_MIN_ADDR=4096 +-CONFIG_NEED_PER_CPU_KM=y +-# CONFIG_CLEANCACHE is not set +-CONFIG_FORCE_MAX_ZONEORDER=11 +-CONFIG_ALIGNMENT_TRAP=y +-# CONFIG_UACCESS_WITH_MEMCPY is not set +-# CONFIG_SECCOMP is not set +-# CONFIG_CC_STACKPROTECTOR is not set +-# CONFIG_DEPRECATED_PARAM_STRUCT is not set +- +-# +-# Boot options +-# +-# CONFIG_USE_OF is not set + CONFIG_ZBOOT_ROM_TEXT=0x0 + CONFIG_ZBOOT_ROM_BSS=0x0 + CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext3 rootwait" +-CONFIG_CMDLINE_FROM_BOOTLOADER=y +-# CONFIG_CMDLINE_EXTEND is not set +-# CONFIG_CMDLINE_FORCE is not set +-# CONFIG_XIP_KERNEL is not set +-# CONFIG_KEXEC is not set +-# CONFIG_CRASH_DUMP is not set +-# CONFIG_AUTO_ZRELADDR is not set +- +-# +-# CPU Power Management +-# + CONFIG_CPU_IDLE=y +-CONFIG_CPU_IDLE_GOV_LADDER=y +-CONFIG_CPU_IDLE_GOV_MENU=y +- +-# +-# Floating point emulation +-# +- +-# +-# At least one emulation must be selected +-# +-# CONFIG_FPE_NWFPE is not set +-# CONFIG_FPE_FASTFPE is not set + CONFIG_VFP=y +- +-# +-# Userspace binary formats +-# +-CONFIG_BINFMT_ELF=y +-CONFIG_HAVE_AOUT=y +-# CONFIG_BINFMT_AOUT is not set +-# CONFIG_BINFMT_MISC is not set +- +-# +-# Power management options +-# +-CONFIG_SUSPEND=y +-CONFIG_SUSPEND_FREEZER=y +-CONFIG_PM_SLEEP=y +-# CONFIG_PM_RUNTIME is not set +-CONFIG_PM=y +-# CONFIG_PM_DEBUG is not set +-# CONFIG_APM_EMULATION is not set +-CONFIG_PM_CLK=y +-CONFIG_ARCH_SUSPEND_POSSIBLE=y ++CONFIG_BINFMT_MISC=m + CONFIG_NET=y +- +-# +-# Networking options +-# + CONFIG_PACKET=y + CONFIG_UNIX=y +-CONFIG_XFRM=y + CONFIG_XFRM_USER=y +-# CONFIG_XFRM_SUB_POLICY is not set +-# CONFIG_XFRM_MIGRATE is not set +-# CONFIG_XFRM_STATISTICS is not set + CONFIG_NET_KEY=m +-# CONFIG_NET_KEY_MIGRATE is not set + CONFIG_INET=y + CONFIG_IP_MULTICAST=y +-# CONFIG_IP_ADVANCED_ROUTER is not set + CONFIG_IP_PNP=y + CONFIG_IP_PNP_DHCP=y +-# CONFIG_IP_PNP_BOOTP is not set + CONFIG_IP_PNP_RARP=y +-# CONFIG_NET_IPIP is not set +-# CONFIG_NET_IPGRE_DEMUX is not set +-# CONFIG_IP_MROUTE is not set +-# CONFIG_ARPD is not set + CONFIG_SYN_COOKIES=y +-# CONFIG_INET_AH is not set +-# CONFIG_INET_ESP is not set +-# CONFIG_INET_IPCOMP is not set +-# CONFIG_INET_XFRM_TUNNEL is not set +-# CONFIG_INET_TUNNEL is not set + # CONFIG_INET_XFRM_MODE_TRANSPORT is not set + # CONFIG_INET_XFRM_MODE_TUNNEL is not set + # CONFIG_INET_XFRM_MODE_BEET is not set + # CONFIG_INET_LRO is not set + # CONFIG_INET_DIAG is not set +-# CONFIG_TCP_CONG_ADVANCED is not set +-CONFIG_TCP_CONG_CUBIC=y +-CONFIG_DEFAULT_TCP_CONG="cubic" +-# CONFIG_TCP_MD5SIG is not set + # CONFIG_IPV6 is not set +-# CONFIG_NETWORK_SECMARK is not set +-# CONFIG_NETWORK_PHY_TIMESTAMPING is not set +-# CONFIG_NETFILTER is not set +-# CONFIG_IP_DCCP is not set +-# CONFIG_IP_SCTP is not set +-# CONFIG_RDS is not set +-# CONFIG_TIPC is not set +-# CONFIG_ATM is not set +-# CONFIG_L2TP is not set +-# CONFIG_BRIDGE is not set +-# CONFIG_NET_DSA is not set +-# CONFIG_VLAN_8021Q is not set +-# CONFIG_DECNET is not set +-# CONFIG_LLC2 is not set +-# CONFIG_IPX is not set +-# CONFIG_ATALK is not set +-# CONFIG_X25 is not set +-# CONFIG_LAPB is not set +-# CONFIG_ECONET is not set +-# CONFIG_WAN_ROUTER is not set +-# CONFIG_PHONET is not set +-# CONFIG_IEEE802154 is not set +-# CONFIG_NET_SCHED is not set +-# CONFIG_DCB is not set +-CONFIG_DNS_RESOLVER=y +-# CONFIG_BATMAN_ADV is not set +- +-# +-# Network testing +-# + CONFIG_NET_PKTGEN=m +-# CONFIG_HAMRADIO is not set +-# CONFIG_CAN is not set +-# CONFIG_IRDA is not set +-# CONFIG_BT is not set +-# CONFIG_AF_RXRPC is not set +-CONFIG_WIRELESS=y +-CONFIG_WEXT_CORE=y +-CONFIG_WEXT_PROC=y +-CONFIG_CFG80211=y +-# CONFIG_NL80211_TESTMODE is not set +-# CONFIG_CFG80211_DEVELOPER_WARNINGS is not set +-# CONFIG_CFG80211_REG_DEBUG is not set +-CONFIG_CFG80211_DEFAULT_PS=y +-# CONFIG_CFG80211_INTERNAL_REGDB is not set +-CONFIG_CFG80211_WEXT=y +-CONFIG_WIRELESS_EXT_SYSFS=y +-# CONFIG_LIB80211 is not set +-# CONFIG_MAC80211 is not set +-# CONFIG_WIMAX is not set +-# CONFIG_RFKILL is not set +-# CONFIG_NET_9P is not set +-# CONFIG_CAIF is not set +-# CONFIG_CEPH_LIB is not set +-# CONFIG_NFC is not set +- +-# +-# Device Drivers +-# +- +-# +-# Generic Driver Options +-# +-CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" +-# CONFIG_DEVTMPFS is not set +-CONFIG_STANDALONE=y +-CONFIG_PREVENT_FIRMWARE_BUILD=y +-CONFIG_FW_LOADER=y +-CONFIG_FIRMWARE_IN_KERNEL=y +-CONFIG_EXTRA_FIRMWARE="" +-# CONFIG_DEBUG_DRIVER is not set +-# CONFIG_DEBUG_DEVRES is not set +-# CONFIG_SYS_HYPERVISOR is not set +-# CONFIG_CONNECTOR is not set +-# CONFIG_MTD is not set +-# CONFIG_PARPORT is not set +-CONFIG_BLK_DEV=y +-# CONFIG_BLK_DEV_COW_COMMON is not set ++CONFIG_IRDA=m ++CONFIG_IRLAN=m ++CONFIG_IRCOMM=m ++CONFIG_IRDA_ULTRA=y ++CONFIG_IRDA_CACHE_LAST_LSAP=y ++CONFIG_IRDA_FAST_RR=y ++CONFIG_IRTTY_SIR=m ++CONFIG_KINGSUN_DONGLE=m ++CONFIG_KSDAZZLE_DONGLE=m ++CONFIG_KS959_DONGLE=m ++CONFIG_USB_IRDA=m ++CONFIG_SIGMATEL_FIR=m ++CONFIG_MCS_FIR=m ++CONFIG_BT=m ++CONFIG_BT_L2CAP=y ++CONFIG_BT_SCO=y ++CONFIG_BT_RFCOMM=m ++CONFIG_BT_RFCOMM_TTY=y ++CONFIG_BT_BNEP=m ++CONFIG_BT_BNEP_MC_FILTER=y ++CONFIG_BT_BNEP_PROTO_FILTER=y ++CONFIG_BT_HIDP=m ++CONFIG_BT_HCIBTUSB=m ++CONFIG_BT_HCIBCM203X=m ++CONFIG_BT_HCIBPA10X=m ++CONFIG_BT_HCIBFUSB=m ++CONFIG_BT_HCIVHCI=m ++CONFIG_BT_MRVL=m ++CONFIG_BT_MRVL_SDIO=m ++CONFIG_BT_ATH3K=m ++CONFIG_CFG80211=m ++CONFIG_MAC80211=m ++CONFIG_MAC80211_RC_PID=y ++CONFIG_MAC80211_MESH=y ++CONFIG_WIMAX=m ++CONFIG_NET_9P=m ++CONFIG_NFC=m ++CONFIG_NFC_PN533=m ++CONFIG_DEVTMPFS=y + CONFIG_BLK_DEV_LOOP=y +-CONFIG_BLK_DEV_LOOP_MIN_COUNT=8 +-# CONFIG_BLK_DEV_CRYPTOLOOP is not set +- +-# +-# DRBD disabled because PROC_FS, INET or CONNECTOR not selected +-# +-# CONFIG_BLK_DEV_NBD is not set +-# CONFIG_BLK_DEV_UB is not set ++CONFIG_BLK_DEV_CRYPTOLOOP=m ++CONFIG_BLK_DEV_NBD=m + CONFIG_BLK_DEV_RAM=y +-CONFIG_BLK_DEV_RAM_COUNT=16 +-CONFIG_BLK_DEV_RAM_SIZE=4096 +-# CONFIG_BLK_DEV_XIP is not set +-# CONFIG_CDROM_PKTCDVD is not set +-# CONFIG_ATA_OVER_ETH is not set +-# CONFIG_MG_DISK is not set +-# CONFIG_BLK_DEV_RBD is not set +-# CONFIG_SENSORS_LIS3LV02D is not set ++CONFIG_CDROM_PKTCDVD=m + CONFIG_MISC_DEVICES=y +-# CONFIG_ENCLOSURE_SERVICES is not set +-# CONFIG_C2PORT is not set +- +-# +-# EEPROM support +-# +-# CONFIG_EEPROM_93CX6 is not set +-# CONFIG_IWMC3200TOP is not set +- +-# +-# Texas Instruments shared transport line discipline +-# +-# CONFIG_TI_ST is not set +-CONFIG_BCM2708_VCHIQ=y +-CONFIG_HAVE_IDE=y +-# CONFIG_IDE is not set +- +-# +-# SCSI device support +-# +-CONFIG_SCSI_MOD=y +-# CONFIG_RAID_ATTRS is not set + CONFIG_SCSI=y +-CONFIG_SCSI_DMA=y +-# CONFIG_SCSI_TGT is not set +-# CONFIG_SCSI_NETLINK is not set + # CONFIG_SCSI_PROC_FS is not set +- +-# +-# SCSI support type (disk, tape, CD-ROM) +-# +-CONFIG_BLK_DEV_SD=m +-# CONFIG_CHR_DEV_ST is not set +-# CONFIG_CHR_DEV_OSST is not set ++CONFIG_BLK_DEV_SD=y + CONFIG_BLK_DEV_SR=m +-# CONFIG_BLK_DEV_SR_VENDOR is not set +-# CONFIG_CHR_DEV_SG is not set +-# CONFIG_CHR_DEV_SCH is not set + CONFIG_SCSI_MULTI_LUN=y +-# CONFIG_SCSI_CONSTANTS is not set +-# CONFIG_SCSI_LOGGING is not set +-# CONFIG_SCSI_SCAN_ASYNC is not set +-CONFIG_SCSI_WAIT_SCAN=m +- +-# +-# SCSI Transports +-# +-# CONFIG_SCSI_SPI_ATTRS is not set +-# CONFIG_SCSI_FC_ATTRS is not set +-# CONFIG_SCSI_ISCSI_ATTRS is not set +-# CONFIG_SCSI_SAS_ATTRS is not set +-# CONFIG_SCSI_SAS_LIBSAS is not set +-# CONFIG_SCSI_SRP_ATTRS is not set + # CONFIG_SCSI_LOWLEVEL is not set +-# CONFIG_SCSI_DH is not set +-# CONFIG_SCSI_OSD_INITIATOR is not set +-# CONFIG_ATA is not set +-# CONFIG_MD is not set +-# CONFIG_TARGET_CORE is not set + CONFIG_NETDEVICES=y +-# CONFIG_DUMMY is not set +-# CONFIG_BONDING is not set +-# CONFIG_MACVLAN is not set +-# CONFIG_EQUALIZER is not set + CONFIG_TUN=m +-# CONFIG_VETH is not set +-CONFIG_MII=y + CONFIG_PHYLIB=m +- +-# +-# MII PHY device drivers +-# +-# CONFIG_MARVELL_PHY is not set +-# CONFIG_DAVICOM_PHY is not set +-# CONFIG_QSEMI_PHY is not set +-# CONFIG_LXT_PHY is not set +-# CONFIG_CICADA_PHY is not set +-# CONFIG_VITESSE_PHY is not set +-# CONFIG_SMSC_PHY is not set +-# CONFIG_BROADCOM_PHY is not set +-# CONFIG_ICPLUS_PHY is not set +-# CONFIG_REALTEK_PHY is not set +-# CONFIG_NATIONAL_PHY is not set +-# CONFIG_STE10XP is not set +-# CONFIG_LSI_ET1011C_PHY is not set +-# CONFIG_MICREL_PHY is not set + CONFIG_MDIO_BITBANG=m +-# CONFIG_MDIO_GPIO is not set + CONFIG_NET_ETHERNET=y +-CONFIG_AX88796=m +-# CONFIG_AX88796_93CX6 is not set +-# CONFIG_SMC91X is not set +-# CONFIG_DM9000 is not set +-# CONFIG_ETHOC is not set +-# CONFIG_SMC911X is not set +-# CONFIG_SMSC911X is not set +-# CONFIG_DNET is not set +-# CONFIG_IBM_NEW_EMAC_ZMII is not set +-# CONFIG_IBM_NEW_EMAC_RGMII is not set +-# CONFIG_IBM_NEW_EMAC_TAH is not set +-# CONFIG_IBM_NEW_EMAC_EMAC4 is not set +-# CONFIG_IBM_NEW_EMAC_NO_FLOW_CTRL is not set +-# CONFIG_IBM_NEW_EMAC_MAL_CLR_ICINTSTAT is not set +-# CONFIG_IBM_NEW_EMAC_MAL_COMMON_ERR is not set +-# CONFIG_B44 is not set +-# CONFIG_KS8851_MLL is not set +-# CONFIG_FTMAC100 is not set + # CONFIG_NETDEV_1000 is not set + # CONFIG_NETDEV_10000 is not set +-# CONFIG_WLAN is not set +- +-# +-# Enable WiMAX (Networking options) to see the WiMAX drivers +-# +- +-# +-# USB Network Adapters +-# +-# CONFIG_USB_CATC is not set +-# CONFIG_USB_KAWETH is not set +-# CONFIG_USB_PEGASUS is not set +-# CONFIG_USB_RTL8150 is not set ++CONFIG_LIBERTAS_THINFIRM=m ++CONFIG_LIBERTAS_THINFIRM_USB=m ++CONFIG_AT76C50X_USB=m ++CONFIG_USB_ZD1201=m ++CONFIG_USB_NET_RNDIS_WLAN=m ++CONFIG_RTL8187=m ++CONFIG_MAC80211_HWSIM=m ++CONFIG_ATH_COMMON=m ++CONFIG_ATH9K=m ++CONFIG_ATH9K_HTC=m ++CONFIG_CARL9170=m ++CONFIG_B43=m ++CONFIG_B43LEGACY=m ++CONFIG_HOSTAP=m ++CONFIG_IWM=m ++CONFIG_LIBERTAS=m ++CONFIG_LIBERTAS_USB=m ++CONFIG_LIBERTAS_SDIO=m ++CONFIG_P54_COMMON=m ++CONFIG_P54_USB=m ++CONFIG_RT2X00=m ++CONFIG_RT2500USB=m ++CONFIG_RT73USB=m ++CONFIG_RT2800USB=m ++CONFIG_RT2800USB_RT53XX=y ++CONFIG_RTL8192CU=m ++CONFIG_WL1251=m ++CONFIG_WL12XX_MENU=m ++CONFIG_ZD1211RW=m ++CONFIG_MWIFIEX=m ++CONFIG_MWIFIEX_SDIO=m ++CONFIG_WIMAX_I2400M_USB=m ++CONFIG_USB_CATC=m ++CONFIG_USB_KAWETH=m ++CONFIG_USB_PEGASUS=m ++CONFIG_USB_RTL8150=m + CONFIG_USB_USBNET=y + CONFIG_USB_NET_AX8817X=m +-# CONFIG_USB_NET_CDCETHER is not set +-# CONFIG_USB_NET_CDC_EEM is not set +-CONFIG_USB_NET_CDC_NCM=y +-# CONFIG_USB_NET_DM9601 is not set +-# CONFIG_USB_NET_SMSC75XX is not set ++CONFIG_USB_NET_CDCETHER=m ++CONFIG_USB_NET_CDC_EEM=m ++CONFIG_USB_NET_DM9601=m ++CONFIG_USB_NET_SMSC75XX=m + CONFIG_USB_NET_SMSC95XX=y +-# CONFIG_USB_NET_GL620A is not set +-# CONFIG_USB_NET_NET1080 is not set +-# CONFIG_USB_NET_PLUSB is not set +-# CONFIG_USB_NET_MCS7830 is not set +-# CONFIG_USB_NET_RNDIS_HOST is not set +-# CONFIG_USB_NET_CDC_SUBSET is not set ++CONFIG_USB_NET_GL620A=m ++CONFIG_USB_NET_NET1080=m ++CONFIG_USB_NET_PLUSB=m ++CONFIG_USB_NET_MCS7830=m ++CONFIG_USB_NET_CDC_SUBSET=m ++CONFIG_USB_ALI_M5632=y ++CONFIG_USB_AN2720=y ++CONFIG_USB_KC2190=y + # CONFIG_USB_NET_ZAURUS is not set +-# CONFIG_USB_NET_CX82310_ETH is not set +-# CONFIG_USB_NET_KALMIA is not set +-# CONFIG_USB_NET_INT51X1 is not set +-# CONFIG_USB_IPHETH is not set +-# CONFIG_USB_SIERRA_NET is not set +-# CONFIG_WAN is not set +- +-# +-# CAIF transport drivers +-# +-# CONFIG_PPP is not set +-# CONFIG_SLIP is not set +-# CONFIG_NETCONSOLE is not set +-# CONFIG_NETPOLL is not set +-# CONFIG_NET_POLL_CONTROLLER is not set +-# CONFIG_ISDN is not set +-# CONFIG_PHONE is not set +- +-# +-# Input device support +-# +-CONFIG_INPUT=y +-# CONFIG_INPUT_FF_MEMLESS is not set +-# CONFIG_INPUT_POLLDEV is not set +-# CONFIG_INPUT_SPARSEKMAP is not set +- +-# +-# Userland interfaces +-# +-CONFIG_INPUT_MOUSEDEV=y ++CONFIG_USB_NET_CX82310_ETH=m ++CONFIG_USB_NET_KALMIA=m ++CONFIG_USB_NET_INT51X1=m ++CONFIG_USB_IPHETH=m ++CONFIG_USB_SIERRA_NET=m ++CONFIG_USB_VL600=m ++CONFIG_PPP=m ++CONFIG_PPP_ASYNC=m ++CONFIG_PPP_SYNC_TTY=m ++CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_BSDCOMP=m ++CONFIG_SLIP=m ++CONFIG_SLIP_COMPRESSED=y ++CONFIG_NETCONSOLE=m ++CONFIG_INPUT_POLLDEV=m + # CONFIG_INPUT_MOUSEDEV_PSAUX is not set +-CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024 +-CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768 +-# CONFIG_INPUT_JOYDEV is not set ++CONFIG_INPUT_JOYDEV=m + CONFIG_INPUT_EVDEV=m +-# CONFIG_INPUT_EVBUG is not set +- +-# +-# Input Device Drivers +-# + # CONFIG_INPUT_KEYBOARD is not set + # CONFIG_INPUT_MOUSE is not set +-# CONFIG_INPUT_JOYSTICK is not set +-# CONFIG_INPUT_TABLET is not set +-# CONFIG_INPUT_TOUCHSCREEN is not set + CONFIG_INPUT_MISC=y +-# CONFIG_INPUT_AD714X is not set +-# CONFIG_INPUT_ATI_REMOTE is not set +-# CONFIG_INPUT_ATI_REMOTE2 is not set +-# CONFIG_INPUT_KEYSPAN_REMOTE is not set +-# CONFIG_INPUT_POWERMATE is not set +-# CONFIG_INPUT_YEALINK is not set +-# CONFIG_INPUT_CM109 is not set ++CONFIG_INPUT_AD714X=m ++CONFIG_INPUT_ATI_REMOTE=m ++CONFIG_INPUT_ATI_REMOTE2=m ++CONFIG_INPUT_KEYSPAN_REMOTE=m ++CONFIG_INPUT_POWERMATE=m ++CONFIG_INPUT_YEALINK=m ++CONFIG_INPUT_CM109=m + CONFIG_INPUT_UINPUT=m +-# CONFIG_INPUT_GPIO_ROTARY_ENCODER is not set +-# CONFIG_INPUT_ADXL34X is not set +-# CONFIG_INPUT_CMA3000 is not set +- +-# +-# Hardware I/O ports +-# ++CONFIG_INPUT_GPIO_ROTARY_ENCODER=m ++CONFIG_INPUT_ADXL34X=m ++CONFIG_INPUT_CMA3000=m + CONFIG_SERIO=m +-CONFIG_SERIO_SERPORT=m +-# CONFIG_SERIO_AMBAKMI is not set +-# CONFIG_SERIO_LIBPS2 is not set + CONFIG_SERIO_RAW=m +-# CONFIG_SERIO_ALTERA_PS2 is not set +-# CONFIG_SERIO_PS2MULT is not set + CONFIG_GAMEPORT=m + CONFIG_GAMEPORT_NS558=m + CONFIG_GAMEPORT_L4=m +- +-# +-# Character devices +-# +-CONFIG_VT=y +-CONFIG_CONSOLE_TRANSLATIONS=y +-CONFIG_VT_CONSOLE=y +-CONFIG_HW_CONSOLE=y + CONFIG_VT_HW_CONSOLE_BINDING=y +-CONFIG_UNIX98_PTYS=y +-# CONFIG_DEVPTS_MULTIPLE_INSTANCES is not set + # CONFIG_LEGACY_PTYS is not set +-# CONFIG_SERIAL_NONSTANDARD is not set +-# CONFIG_N_GSM is not set +-# CONFIG_TRACE_SINK is not set + # CONFIG_DEVKMEM is not set +- +-# +-# Serial drivers +-# +-# CONFIG_SERIAL_8250 is not set +- +-# +-# Non-8250 serial port support +-# +-# CONFIG_SERIAL_AMBA_PL010 is not set + CONFIG_SERIAL_AMBA_PL011=y + CONFIG_SERIAL_AMBA_PL011_CONSOLE=y +-CONFIG_SERIAL_CORE=y +-CONFIG_SERIAL_CORE_CONSOLE=y +-# CONFIG_SERIAL_TIMBERDALE is not set +-# CONFIG_SERIAL_ALTERA_JTAGUART is not set +-# CONFIG_SERIAL_ALTERA_UART is not set +-# CONFIG_SERIAL_XILINX_PS_UART is not set +-# CONFIG_TTY_PRINTK is not set +-# CONFIG_HVC_DCC is not set +-# CONFIG_IPMI_HANDLER is not set + # CONFIG_HW_RANDOM is not set +-# CONFIG_R3964 is not set + CONFIG_RAW_DRIVER=y +-CONFIG_MAX_RAW_DEVS=256 +-# CONFIG_TCG_TPM is not set +-# CONFIG_RAMOOPS is not set +-# CONFIG_I2C is not set +-# CONFIG_SPI is not set +- +-# +-# PPS support +-# +-# CONFIG_PPS is not set +- +-# +-# PPS generators support +-# +- +-# +-# PTP clock support +-# +- +-# +-# Enable Device Drivers -> PPS to see the PTP clock options. +-# +-CONFIG_ARCH_REQUIRE_GPIOLIB=y +-CONFIG_GPIOLIB=y +-# CONFIG_DEBUG_GPIO is not set + CONFIG_GPIO_SYSFS=y +- +-# +-# Memory mapped GPIO drivers: +-# +-# CONFIG_GPIO_GENERIC_PLATFORM is not set +-# CONFIG_GPIO_IT8761E is not set +-# CONFIG_GPIO_PL061 is not set +- +-# +-# I2C GPIO expanders: +-# +- +-# +-# PCI GPIO expanders: +-# +- +-# +-# SPI GPIO expanders: +-# +- +-# +-# AC97 GPIO expanders: +-# +- +-# +-# MODULbus GPIO expanders: +-# +-# CONFIG_W1 is not set +-# CONFIG_POWER_SUPPLY is not set + # CONFIG_HWMON is not set +-# CONFIG_THERMAL is not set +-# CONFIG_WATCHDOG is not set +-CONFIG_SSB_POSSIBLE=y +- +-# +-# Sonics Silicon Backplane +-# +-# CONFIG_SSB is not set +-CONFIG_BCMA_POSSIBLE=y +- +-# +-# Broadcom specific AMBA +-# +-# CONFIG_BCMA is not set ++CONFIG_WATCHDOG=y ++CONFIG_BCM2708_WDT=m + # CONFIG_MFD_SUPPORT is not set +-# CONFIG_REGULATOR is not set +-# CONFIG_MEDIA_SUPPORT is not set +- +-# +-# Graphics support +-# +-# CONFIG_DRM is not set +-# CONFIG_VGASTATE is not set +-# CONFIG_VIDEO_OUTPUT_CONTROL is not set + CONFIG_FB=y +-# CONFIG_FIRMWARE_EDID is not set +-# CONFIG_FB_DDC is not set +-# CONFIG_FB_BOOT_VESA_SUPPORT is not set +-CONFIG_FB_CFB_FILLRECT=y +-CONFIG_FB_CFB_COPYAREA=y +-CONFIG_FB_CFB_IMAGEBLIT=y +-# CONFIG_FB_CFB_REV_PIXELS_IN_BYTE is not set +-# CONFIG_FB_SYS_FILLRECT is not set +-# CONFIG_FB_SYS_COPYAREA is not set +-# CONFIG_FB_SYS_IMAGEBLIT is not set +-# CONFIG_FB_FOREIGN_ENDIAN is not set +-# CONFIG_FB_SYS_FOPS is not set +-# CONFIG_FB_WMT_GE_ROPS is not set +-# CONFIG_FB_SVGALIB is not set +-# CONFIG_FB_MACMODES is not set +-# CONFIG_FB_BACKLIGHT is not set +-# CONFIG_FB_MODE_HELPERS is not set +-# CONFIG_FB_TILEBLITTING is not set +- +-# +-# Frame buffer hardware drivers +-# + CONFIG_FB_BCM2708=y +-# CONFIG_FB_ARMCLCD is not set +-# CONFIG_FB_S1D13XXX is not set +-# CONFIG_FB_UDL is not set +-# CONFIG_FB_VIRTUAL is not set +-# CONFIG_FB_METRONOME is not set +-# CONFIG_FB_BROADSHEET is not set +-# CONFIG_BACKLIGHT_LCD_SUPPORT is not set +- +-# +-# Display device support +-# +-# CONFIG_DISPLAY_SUPPORT is not set +- +-# +-# Console display driver support +-# +-CONFIG_DUMMY_CONSOLE=y + CONFIG_FRAMEBUFFER_CONSOLE=y +-# CONFIG_FRAMEBUFFER_CONSOLE_DETECT_PRIMARY is not set +-# CONFIG_FRAMEBUFFER_CONSOLE_ROTATION is not set +-# CONFIG_FONTS is not set +-CONFIG_FONT_8x8=y +-CONFIG_FONT_8x16=y + CONFIG_LOGO=y + # CONFIG_LOGO_LINUX_MONO is not set + # CONFIG_LOGO_LINUX_VGA16 is not set +-CONFIG_LOGO_LINUX_CLUT224=y +-# CONFIG_SOUND is not set +-CONFIG_HID_SUPPORT=y +-CONFIG_HID=y +-# CONFIG_HIDRAW is not set +- +-# +-# USB Input Devices +-# +-CONFIG_USB_HID=y ++CONFIG_SOUND=y ++CONFIG_SND=m ++CONFIG_SND_SEQUENCER=m ++CONFIG_SND_SEQ_DUMMY=m ++CONFIG_SND_MIXER_OSS=m ++CONFIG_SND_PCM_OSS=m ++CONFIG_SND_SEQUENCER_OSS=y ++CONFIG_SND_HRTIMER=m ++CONFIG_SND_DUMMY=m ++CONFIG_SND_ALOOP=m ++CONFIG_SND_VIRMIDI=m ++CONFIG_SND_MTPAV=m ++CONFIG_SND_SERIAL_U16550=m ++CONFIG_SND_MPU401=m ++CONFIG_SND_BCM2835=m ++CONFIG_SND_USB_AUDIO=m ++CONFIG_SND_USB_UA101=m ++CONFIG_SND_USB_CAIAQ=m ++CONFIG_SND_USB_6FIRE=m ++CONFIG_SOUND_PRIME=m + CONFIG_HID_PID=y + CONFIG_USB_HIDDEV=y +- +-# +-# Special HID drivers +-# + CONFIG_HID_A4TECH=m +-# CONFIG_HID_ACRUX is not set ++CONFIG_HID_ACRUX=m + CONFIG_HID_APPLE=m + CONFIG_HID_BELKIN=m + CONFIG_HID_CHERRY=m + CONFIG_HID_CHICONY=m + CONFIG_HID_CYPRESS=m + CONFIG_HID_DRAGONRISE=m +-# CONFIG_DRAGONRISE_FF is not set +-# CONFIG_HID_EMS_FF is not set ++CONFIG_HID_EMS_FF=m ++CONFIG_HID_ELECOM=m + CONFIG_HID_EZKEY=m +-# CONFIG_HID_HOLTEK is not set +-# CONFIG_HID_KEYTOUCH is not set ++CONFIG_HID_HOLTEK=m ++CONFIG_HID_KEYTOUCH=m + CONFIG_HID_KYE=m +-# CONFIG_HID_UCLOGIC is not set +-# CONFIG_HID_WALTOP is not set ++CONFIG_HID_UCLOGIC=m ++CONFIG_HID_WALTOP=m + CONFIG_HID_GYRATION=m + CONFIG_HID_TWINHAN=m + CONFIG_HID_KENSINGTON=m +-# CONFIG_HID_LCPOWER is not set ++CONFIG_HID_LCPOWER=m + CONFIG_HID_LOGITECH=m +-# CONFIG_LOGITECH_FF is not set +-# CONFIG_LOGIRUMBLEPAD2_FF is not set +-# CONFIG_LOGIG940_FF is not set +-# CONFIG_LOGIWII_FF is not set ++CONFIG_HID_MAGICMOUSE=m + CONFIG_HID_MICROSOFT=m + CONFIG_HID_MONTEREY=m +-# CONFIG_HID_MULTITOUCH is not set +-# CONFIG_HID_NTRIG is not set ++CONFIG_HID_MULTITOUCH=m ++CONFIG_HID_NTRIG=m + CONFIG_HID_ORTEK=m + CONFIG_HID_PANTHERLORD=m +-# CONFIG_PANTHERLORD_FF is not set + CONFIG_HID_PETALYNX=m +-# CONFIG_HID_PICOLCD is not set +-# CONFIG_HID_QUANTA is not set +-# CONFIG_HID_ROCCAT is not set ++CONFIG_HID_PICOLCD=m ++CONFIG_HID_QUANTA=m ++CONFIG_HID_ROCCAT=m + CONFIG_HID_SAMSUNG=m + CONFIG_HID_SONY=m +-# CONFIG_HID_SPEEDLINK is not set ++CONFIG_HID_SPEEDLINK=m + CONFIG_HID_SUNPLUS=m + CONFIG_HID_GREENASIA=m +-# CONFIG_GREENASIA_FF is not set + CONFIG_HID_SMARTJOYPLUS=m +-# CONFIG_SMARTJOYPLUS_FF is not set + CONFIG_HID_TOPSEED=m + CONFIG_HID_THRUSTMASTER=m +-# CONFIG_THRUSTMASTER_FF is not set ++CONFIG_HID_WACOM=m ++CONFIG_HID_WIIMOTE=m + CONFIG_HID_ZEROPLUS=m +-# CONFIG_ZEROPLUS_FF is not set +-# CONFIG_HID_ZYDACRON is not set +-CONFIG_USB_SUPPORT=y +-CONFIG_USB_ARCH_HAS_HCD=y +-# CONFIG_USB_ARCH_HAS_OHCI is not set +-# CONFIG_USB_ARCH_HAS_EHCI is not set ++CONFIG_HID_ZYDACRON=m + CONFIG_USB=y +-# CONFIG_USB_DEBUG is not set + CONFIG_USB_ANNOUNCE_NEW_DEVICES=y +- +-# +-# Miscellaneous USB options +-# +-# CONFIG_USB_DEVICEFS is not set +-CONFIG_USB_DEVICE_CLASS=y +-# CONFIG_USB_DYNAMIC_MINORS is not set +-# CONFIG_USB_OTG_WHITELIST is not set +-# CONFIG_USB_OTG_BLACKLIST_HUB is not set + CONFIG_USB_MON=m +-# CONFIG_USB_WUSB is not set +-# CONFIG_USB_WUSB_CBAF is not set +- +-# +-# USB Host Controller Drivers +-# +-# CONFIG_USB_C67X00_HCD is not set +-# CONFIG_USB_OXU210HP_HCD is not set +-# CONFIG_USB_ISP116X_HCD is not set +-# CONFIG_USB_ISP1760_HCD is not set +-# CONFIG_USB_ISP1362_HCD is not set +-# CONFIG_USB_SL811_HCD is not set +-# CONFIG_USB_R8A66597_HCD is not set +-# CONFIG_USB_HWA_HCD is not set + CONFIG_USB_DWCOTG=y +- +-# +-# USB Device Class drivers +-# +-# CONFIG_USB_ACM is not set +-# CONFIG_USB_PRINTER is not set +-# CONFIG_USB_WDM is not set +-# CONFIG_USB_TMC is not set +- +-# +-# NOTE: USB_STORAGE depends on SCSI but BLK_DEV_SD may +-# +- +-# +-# also be needed; see USB_STORAGE Help for more info +-# + CONFIG_USB_STORAGE=y +-# CONFIG_USB_STORAGE_DEBUG is not set +-# CONFIG_USB_STORAGE_REALTEK is not set +-# CONFIG_USB_STORAGE_DATAFAB is not set +-# CONFIG_USB_STORAGE_FREECOM is not set +-# CONFIG_USB_STORAGE_ISD200 is not set +-# CONFIG_USB_STORAGE_USBAT is not set +-# CONFIG_USB_STORAGE_SDDR09 is not set +-# CONFIG_USB_STORAGE_SDDR55 is not set +-# CONFIG_USB_STORAGE_JUMPSHOT is not set +-# CONFIG_USB_STORAGE_ALAUDA is not set +-# CONFIG_USB_STORAGE_ONETOUCH is not set +-# CONFIG_USB_STORAGE_KARMA is not set +-# CONFIG_USB_STORAGE_CYPRESS_ATACB is not set +-# CONFIG_USB_STORAGE_ENE_UB6250 is not set +-# CONFIG_USB_UAS is not set ++CONFIG_USB_STORAGE_REALTEK=m ++CONFIG_USB_STORAGE_DATAFAB=m ++CONFIG_USB_STORAGE_FREECOM=m ++CONFIG_USB_STORAGE_ISD200=m ++CONFIG_USB_STORAGE_USBAT=m ++CONFIG_USB_STORAGE_SDDR09=m ++CONFIG_USB_STORAGE_SDDR55=m ++CONFIG_USB_STORAGE_JUMPSHOT=m ++CONFIG_USB_STORAGE_ALAUDA=m ++CONFIG_USB_STORAGE_ONETOUCH=m ++CONFIG_USB_STORAGE_KARMA=m ++CONFIG_USB_STORAGE_CYPRESS_ATACB=m ++CONFIG_USB_STORAGE_ENE_UB6250=m ++CONFIG_USB_UAS=y + CONFIG_USB_LIBUSUAL=y +- +-# +-# USB Imaging devices +-# +-# CONFIG_USB_MDC800 is not set +-# CONFIG_USB_MICROTEK is not set +- +-# +-# USB port drivers +-# +-# CONFIG_USB_SERIAL is not set +- +-# +-# USB Miscellaneous drivers +-# +-# CONFIG_USB_EMI62 is not set +-# CONFIG_USB_EMI26 is not set +-# CONFIG_USB_ADUTUX is not set +-# CONFIG_USB_SEVSEG is not set +-# CONFIG_USB_RIO500 is not set +-# CONFIG_USB_LEGOTOWER is not set +-# CONFIG_USB_LCD is not set +-# CONFIG_USB_LED is not set +-# CONFIG_USB_CYPRESS_CY7C63 is not set +-# CONFIG_USB_CYTHERM is not set +-# CONFIG_USB_IDMOUSE is not set +-# CONFIG_USB_FTDI_ELAN is not set +-# CONFIG_USB_APPLEDISPLAY is not set +-# CONFIG_USB_LD is not set +-# CONFIG_USB_TRANCEVIBRATOR is not set +-# CONFIG_USB_IOWARRIOR is not set +-# CONFIG_USB_TEST is not set +-# CONFIG_USB_ISIGHTFW is not set +-# CONFIG_USB_YUREX is not set +-# CONFIG_USB_GADGET is not set +- +-# +-# OTG and related infrastructure +-# +-# CONFIG_USB_GPIO_VBUS is not set +-# CONFIG_USB_ULPI is not set +-# CONFIG_NOP_USB_XCEIV is not set ++CONFIG_USB_MDC800=m ++CONFIG_USB_MICROTEK=m ++CONFIG_USB_SERIAL=m ++CONFIG_USB_SERIAL_GENERIC=y ++CONFIG_USB_SERIAL_AIRCABLE=m ++CONFIG_USB_SERIAL_ARK3116=m ++CONFIG_USB_SERIAL_BELKIN=m ++CONFIG_USB_SERIAL_CH341=m ++CONFIG_USB_SERIAL_WHITEHEAT=m ++CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m ++CONFIG_USB_SERIAL_CP210X=m ++CONFIG_USB_SERIAL_CYPRESS_M8=m ++CONFIG_USB_SERIAL_EMPEG=m ++CONFIG_USB_SERIAL_FTDI_SIO=m ++CONFIG_USB_SERIAL_FUNSOFT=m ++CONFIG_USB_SERIAL_VISOR=m ++CONFIG_USB_SERIAL_IPAQ=m ++CONFIG_USB_SERIAL_IR=m ++CONFIG_USB_SERIAL_EDGEPORT=m ++CONFIG_USB_SERIAL_EDGEPORT_TI=m ++CONFIG_USB_SERIAL_GARMIN=m ++CONFIG_USB_SERIAL_IPW=m ++CONFIG_USB_SERIAL_IUU=m ++CONFIG_USB_SERIAL_KEYSPAN_PDA=m ++CONFIG_USB_SERIAL_KEYSPAN=m ++CONFIG_USB_SERIAL_KLSI=m ++CONFIG_USB_SERIAL_KOBIL_SCT=m ++CONFIG_USB_SERIAL_MCT_U232=m ++CONFIG_USB_SERIAL_MOS7720=m ++CONFIG_USB_SERIAL_MOS7840=m ++CONFIG_USB_SERIAL_MOTOROLA=m ++CONFIG_USB_SERIAL_NAVMAN=m ++CONFIG_USB_SERIAL_PL2303=m ++CONFIG_USB_SERIAL_OTI6858=m ++CONFIG_USB_SERIAL_QCAUX=m ++CONFIG_USB_SERIAL_QUALCOMM=m ++CONFIG_USB_SERIAL_SPCP8X5=m ++CONFIG_USB_SERIAL_HP4X=m ++CONFIG_USB_SERIAL_SAFE=m ++CONFIG_USB_SERIAL_SIEMENS_MPI=m ++CONFIG_USB_SERIAL_SIERRAWIRELESS=m ++CONFIG_USB_SERIAL_SYMBOL=m ++CONFIG_USB_SERIAL_TI=m ++CONFIG_USB_SERIAL_CYBERJACK=m ++CONFIG_USB_SERIAL_XIRCOM=m ++CONFIG_USB_SERIAL_OPTION=m ++CONFIG_USB_SERIAL_OMNINET=m ++CONFIG_USB_SERIAL_OPTICON=m ++CONFIG_USB_SERIAL_VIVOPAY_SERIAL=m ++CONFIG_USB_SERIAL_ZIO=m ++CONFIG_USB_SERIAL_SSU100=m ++CONFIG_USB_SERIAL_DEBUG=m ++CONFIG_USB_EMI62=m ++CONFIG_USB_EMI26=m ++CONFIG_USB_ADUTUX=m ++CONFIG_USB_SEVSEG=m ++CONFIG_USB_RIO500=m ++CONFIG_USB_LEGOTOWER=m ++CONFIG_USB_LCD=m ++CONFIG_USB_LED=m ++CONFIG_USB_CYPRESS_CY7C63=m ++CONFIG_USB_CYTHERM=m ++CONFIG_USB_IDMOUSE=m ++CONFIG_USB_FTDI_ELAN=m ++CONFIG_USB_APPLEDISPLAY=m ++CONFIG_USB_LD=m ++CONFIG_USB_TRANCEVIBRATOR=m ++CONFIG_USB_IOWARRIOR=m ++CONFIG_USB_TEST=m ++CONFIG_USB_ISIGHTFW=m ++CONFIG_USB_YUREX=m + CONFIG_MMC=y +-# CONFIG_MMC_DEBUG is not set +-# CONFIG_MMC_UNSAFE_RESUME is not set +-# CONFIG_MMC_CLKGATE is not set +- +-# +-# MMC/SD/SDIO Card Drivers +-# +-CONFIG_MMC_BLOCK=y +-CONFIG_MMC_BLOCK_MINORS=8 +-CONFIG_MMC_BLOCK_BOUNCE=y +-# CONFIG_SDIO_UART is not set +-# CONFIG_MMC_TEST is not set +- +-# +-# MMC/SD/SDIO Host Controller Drivers +-# +-# CONFIG_MMC_ARMMMCI is not set + CONFIG_MMC_SDHCI=y +-CONFIG_MMC_SDHCI_IO_ACCESSORS=y + CONFIG_MMC_SDHCI_PLTFM=y +-# CONFIG_MMC_SDHCI_PXAV3 is not set +-# CONFIG_MMC_SDHCI_PXAV2 is not set + CONFIG_MMC_SDHCI_BCM2708=y + CONFIG_MMC_SDHCI_BCM2708_DMA=y +-# CONFIG_MMC_BCM2708 is not set +-# CONFIG_MMC_DW is not set +-# CONFIG_MMC_VUB300 is not set +-# CONFIG_MMC_USHC is not set +-# CONFIG_MEMSTICK is not set +-CONFIG_NEW_LEDS=y +-CONFIG_LEDS_CLASS=y +- +-# +-# LED drivers +-# + CONFIG_LEDS_GPIO=y +-# CONFIG_LEDS_LT3593 is not set +-CONFIG_LEDS_TRIGGERS=y +- +-# +-# LED Triggers +-# + CONFIG_LEDS_TRIGGER_TIMER=m + CONFIG_LEDS_TRIGGER_HEARTBEAT=m +-# CONFIG_LEDS_TRIGGER_BACKLIGHT is not set +-# CONFIG_LEDS_TRIGGER_GPIO is not set + CONFIG_LEDS_TRIGGER_DEFAULT_ON=m +- +-# +-# iptables trigger is under Netfilter config (LED target) +-# +-# CONFIG_ACCESSIBILITY is not set +-CONFIG_RTC_LIB=y +-# CONFIG_RTC_CLASS is not set +-# CONFIG_DMADEVICES is not set +-# CONFIG_AUXDISPLAY is not set +-# CONFIG_UIO is not set +- +-# +-# Virtio drivers +-# +-# CONFIG_VIRTIO_BALLOON is not set +-# CONFIG_STAGING is not set +-CONFIG_CLKDEV_LOOKUP=y ++CONFIG_UIO=m ++CONFIG_UIO_PDRV=m ++CONFIG_UIO_PDRV_GENIRQ=m + # CONFIG_IOMMU_SUPPORT is not set +-# CONFIG_VIRT_DRIVERS is not set +- +-# +-# File systems +-# +-CONFIG_EXT2_FS=m +-CONFIG_EXT2_FS_XATTR=y +-CONFIG_EXT2_FS_POSIX_ACL=y +-CONFIG_EXT2_FS_SECURITY=y +-CONFIG_EXT2_FS_XIP=y +-CONFIG_EXT3_FS=y +-# CONFIG_EXT3_DEFAULTS_TO_ORDERED is not set +-CONFIG_EXT3_FS_XATTR=y +-CONFIG_EXT3_FS_POSIX_ACL=y +-CONFIG_EXT3_FS_SECURITY=y +-CONFIG_EXT4_FS=m +-CONFIG_EXT4_FS_XATTR=y ++CONFIG_EXT4_FS=y + CONFIG_EXT4_FS_POSIX_ACL=y + CONFIG_EXT4_FS_SECURITY=y +-# CONFIG_EXT4_DEBUG is not set +-CONFIG_FS_XIP=y +-CONFIG_JBD=y +-CONFIG_JBD2=m +-CONFIG_FS_MBCACHE=y +-# CONFIG_REISERFS_FS is not set +-# CONFIG_JFS_FS is not set +-# CONFIG_XFS_FS is not set +-# CONFIG_GFS2_FS is not set +-# CONFIG_OCFS2_FS is not set +-# CONFIG_BTRFS_FS is not set +-# CONFIG_NILFS2_FS is not set +-CONFIG_FS_POSIX_ACL=y +-CONFIG_FILE_LOCKING=y +-CONFIG_FSNOTIFY=y +-CONFIG_DNOTIFY=y +-CONFIG_INOTIFY_USER=y +-# CONFIG_FANOTIFY is not set +-# CONFIG_QUOTA is not set +-# CONFIG_QUOTACTL is not set ++CONFIG_REISERFS_FS=m ++CONFIG_REISERFS_FS_XATTR=y ++CONFIG_REISERFS_FS_POSIX_ACL=y ++CONFIG_REISERFS_FS_SECURITY=y ++CONFIG_JFS_FS=m ++CONFIG_JFS_POSIX_ACL=y ++CONFIG_JFS_SECURITY=y ++CONFIG_XFS_FS=m ++CONFIG_XFS_QUOTA=y ++CONFIG_XFS_POSIX_ACL=y ++CONFIG_XFS_RT=y ++CONFIG_GFS2_FS=m ++CONFIG_OCFS2_FS=m ++CONFIG_BTRFS_FS=m ++CONFIG_BTRFS_FS_POSIX_ACL=y ++CONFIG_NILFS2_FS=m + CONFIG_AUTOFS4_FS=y + CONFIG_FUSE_FS=m + CONFIG_CUSE=m +- +-# +-# Caches +-# + CONFIG_FSCACHE=y +-# CONFIG_FSCACHE_STATS is not set +-# CONFIG_FSCACHE_HISTOGRAM is not set +-# CONFIG_FSCACHE_DEBUG is not set +-# CONFIG_FSCACHE_OBJECT_LIST is not set + CONFIG_CACHEFILES=y +-# CONFIG_CACHEFILES_DEBUG is not set +-# CONFIG_CACHEFILES_HISTOGRAM is not set +- +-# +-# CD-ROM/DVD Filesystems +-# + CONFIG_ISO9660_FS=m + CONFIG_JOLIET=y + CONFIG_ZISOFS=y + CONFIG_UDF_FS=m +-CONFIG_UDF_NLS=y +- +-# +-# DOS/FAT/NT Filesystems +-# +-CONFIG_FAT_FS=y + CONFIG_MSDOS_FS=y + CONFIG_VFAT_FS=y +-CONFIG_FAT_DEFAULT_CODEPAGE=437 + CONFIG_FAT_DEFAULT_IOCHARSET="ascii" + CONFIG_NTFS_FS=m +-# CONFIG_NTFS_DEBUG is not set +-# CONFIG_NTFS_RW is not set +- +-# +-# Pseudo filesystems +-# +-CONFIG_PROC_FS=y +-CONFIG_PROC_SYSCTL=y +-CONFIG_PROC_PAGE_MONITOR=y +-CONFIG_SYSFS=y + CONFIG_TMPFS=y +-# CONFIG_TMPFS_POSIX_ACL is not set +-# CONFIG_TMPFS_XATTR is not set +-# CONFIG_HUGETLB_PAGE is not set ++CONFIG_TMPFS_POSIX_ACL=y + CONFIG_CONFIGFS_FS=y +-CONFIG_MISC_FILESYSTEMS=y +-# CONFIG_ADFS_FS is not set +-# CONFIG_AFFS_FS is not set +-# CONFIG_ECRYPT_FS is not set +-# CONFIG_HFS_FS is not set +-# CONFIG_HFSPLUS_FS is not set +-# CONFIG_BEFS_FS is not set +-# CONFIG_BFS_FS is not set +-# CONFIG_EFS_FS is not set +-# CONFIG_LOGFS is not set +-# CONFIG_CRAMFS is not set +-# CONFIG_SQUASHFS is not set +-# CONFIG_VXFS_FS is not set +-# CONFIG_MINIX_FS is not set +-# CONFIG_OMFS_FS is not set +-# CONFIG_HPFS_FS is not set +-# CONFIG_QNX4FS_FS is not set +-# CONFIG_ROMFS_FS is not set +-# CONFIG_PSTORE is not set +-# CONFIG_SYSV_FS is not set +-# CONFIG_UFS_FS is not set +-CONFIG_NETWORK_FILESYSTEMS=y ++CONFIG_SQUASHFS=m ++CONFIG_SQUASHFS_XATTR=y ++CONFIG_SQUASHFS_LZO=y ++CONFIG_SQUASHFS_XZ=y + CONFIG_NFS_FS=y + CONFIG_NFS_V3=y + CONFIG_NFS_V3_ACL=y + CONFIG_NFS_V4=y +-# CONFIG_NFS_V4_1 is not set + CONFIG_ROOT_NFS=y + CONFIG_NFS_FSCACHE=y +-# CONFIG_NFS_USE_LEGACY_DNS is not set +-CONFIG_NFS_USE_KERNEL_DNS=y +-# CONFIG_NFS_USE_NEW_IDMAPPER is not set +-# CONFIG_NFSD is not set +-CONFIG_LOCKD=y +-CONFIG_LOCKD_V4=y +-CONFIG_NFS_ACL_SUPPORT=y +-CONFIG_NFS_COMMON=y +-CONFIG_SUNRPC=y +-CONFIG_SUNRPC_GSS=y +-# CONFIG_CEPH_FS is not set + CONFIG_CIFS=m +-# CONFIG_CIFS_STATS is not set + CONFIG_CIFS_WEAK_PW_HASH=y +-# CONFIG_CIFS_UPCALL is not set + CONFIG_CIFS_XATTR=y + CONFIG_CIFS_POSIX=y +-# CONFIG_CIFS_DEBUG2 is not set +-# CONFIG_CIFS_DFS_UPCALL is not set +-# CONFIG_CIFS_FSCACHE is not set +-# CONFIG_CIFS_ACL is not set +-# CONFIG_NCP_FS is not set +-# CONFIG_CODA_FS is not set +-# CONFIG_AFS_FS is not set +- +-# +-# Partition Types +-# ++CONFIG_9P_FS=m + CONFIG_PARTITION_ADVANCED=y +-# CONFIG_ACORN_PARTITION is not set +-# CONFIG_OSF_PARTITION is not set +-# CONFIG_AMIGA_PARTITION is not set +-# CONFIG_ATARI_PARTITION is not set + CONFIG_MAC_PARTITION=y +-CONFIG_MSDOS_PARTITION=y +-# CONFIG_BSD_DISKLABEL is not set +-# CONFIG_MINIX_SUBPARTITION is not set +-# CONFIG_SOLARIS_X86_PARTITION is not set +-# CONFIG_UNIXWARE_DISKLABEL is not set +-# CONFIG_LDM_PARTITION is not set +-# CONFIG_SGI_PARTITION is not set +-# CONFIG_ULTRIX_PARTITION is not set +-# CONFIG_SUN_PARTITION is not set +-# CONFIG_KARMA_PARTITION is not set + CONFIG_EFI_PARTITION=y +-# CONFIG_SYSV68_PARTITION is not set +-CONFIG_NLS=y + CONFIG_NLS_DEFAULT="utf8" + CONFIG_NLS_CODEPAGE_437=y + CONFIG_NLS_CODEPAGE_737=m +@@ -1341,218 +470,25 @@ CONFIG_NLS_ISO8859_15=m + CONFIG_NLS_KOI8_R=m + CONFIG_NLS_KOI8_U=m + CONFIG_NLS_UTF8=m +-# CONFIG_DLM is not set +- +-# +-# Kernel hacking +-# +-# CONFIG_PRINTK_TIME is not set +-CONFIG_DEFAULT_MESSAGE_LOGLEVEL=4 +-CONFIG_ENABLE_WARN_DEPRECATED=y +-CONFIG_ENABLE_MUST_CHECK=y +-CONFIG_FRAME_WARN=1024 +-# CONFIG_MAGIC_SYSRQ is not set +-# CONFIG_STRIP_ASM_SYMS is not set +-# CONFIG_UNUSED_SYMBOLS is not set +-# CONFIG_DEBUG_FS is not set +-# CONFIG_HEADERS_CHECK is not set +-# CONFIG_DEBUG_SECTION_MISMATCH is not set +-CONFIG_DEBUG_KERNEL=y +-# CONFIG_DEBUG_SHIRQ is not set +-# CONFIG_LOCKUP_DETECTOR is not set +-# CONFIG_HARDLOCKUP_DETECTOR is not set +-# CONFIG_DETECT_HUNG_TASK is not set + # CONFIG_SCHED_DEBUG is not set +-# CONFIG_SCHEDSTATS is not set +-# CONFIG_TIMER_STATS is not set +-# CONFIG_DEBUG_OBJECTS is not set +-# CONFIG_DEBUG_SLAB is not set +-# CONFIG_DEBUG_KMEMLEAK is not set +-# CONFIG_DEBUG_RT_MUTEXES is not set +-# CONFIG_RT_MUTEX_TESTER is not set +-# CONFIG_DEBUG_SPINLOCK is not set +-# CONFIG_DEBUG_MUTEXES is not set +-# CONFIG_DEBUG_LOCK_ALLOC is not set +-# CONFIG_PROVE_LOCKING is not set +-# CONFIG_SPARSE_RCU_POINTER is not set +-# CONFIG_LOCK_STAT is not set +-# CONFIG_DEBUG_ATOMIC_SLEEP is not set +-# CONFIG_DEBUG_LOCKING_API_SELFTESTS is not set +-# CONFIG_DEBUG_STACK_USAGE is not set +-# CONFIG_DEBUG_KOBJECT is not set + # CONFIG_DEBUG_BUGVERBOSE is not set +-# CONFIG_DEBUG_INFO is not set +-# CONFIG_DEBUG_VM is not set +-# CONFIG_DEBUG_WRITECOUNT is not set +-# CONFIG_DEBUG_MEMORY_INIT is not set +-# CONFIG_DEBUG_LIST is not set +-# CONFIG_TEST_LIST_SORT is not set +-# CONFIG_DEBUG_SG is not set +-# CONFIG_DEBUG_NOTIFIERS is not set +-# CONFIG_DEBUG_CREDENTIALS is not set +-CONFIG_FRAME_POINTER=y +-# CONFIG_BOOT_PRINTK_DELAY is not set +-# CONFIG_RCU_TORTURE_TEST is not set +-# CONFIG_BACKTRACE_SELF_TEST is not set +-# CONFIG_DEBUG_BLOCK_EXT_DEVT is not set +-# CONFIG_DEBUG_FORCE_WEAK_PER_CPU is not set +-# CONFIG_FAULT_INJECTION is not set +-# CONFIG_LATENCYTOP is not set +-# CONFIG_SYSCTL_SYSCALL_CHECK is not set +-# CONFIG_DEBUG_PAGEALLOC is not set +-CONFIG_HAVE_FUNCTION_TRACER=y +-CONFIG_HAVE_FUNCTION_GRAPH_TRACER=y +-CONFIG_HAVE_DYNAMIC_FTRACE=y +-CONFIG_HAVE_FTRACE_MCOUNT_RECORD=y +-CONFIG_HAVE_C_RECORDMCOUNT=y +-CONFIG_TRACING_SUPPORT=y + # CONFIG_FTRACE is not set +-# CONFIG_DMA_API_DEBUG is not set +-# CONFIG_ATOMIC64_SELFTEST is not set +-# CONFIG_SAMPLES is not set +-CONFIG_HAVE_ARCH_KGDB=y +-# CONFIG_KGDB is not set +-# CONFIG_TEST_KSTRTOX is not set +-# CONFIG_STRICT_DEVMEM is not set + # CONFIG_ARM_UNWIND is not set +-# CONFIG_DEBUG_USER is not set +-# CONFIG_DEBUG_LL is not set +-# CONFIG_OC_ETM is not set +- +-# +-# Security options +-# +-CONFIG_KEYS=y +-# CONFIG_KEYS_DEBUG_PROC_KEYS is not set +-# CONFIG_SECURITY_DMESG_RESTRICT is not set +-# CONFIG_SECURITY is not set +-# CONFIG_SECURITYFS is not set +-CONFIG_DEFAULT_SECURITY_DAC=y +-CONFIG_DEFAULT_SECURITY="" +-CONFIG_CRYPTO=y +- +-# +-# Crypto core or helper +-# +-CONFIG_CRYPTO_ALGAPI=y +-CONFIG_CRYPTO_ALGAPI2=y +-CONFIG_CRYPTO_AEAD=m +-CONFIG_CRYPTO_AEAD2=y +-CONFIG_CRYPTO_BLKCIPHER=y +-CONFIG_CRYPTO_BLKCIPHER2=y +-CONFIG_CRYPTO_HASH=y +-CONFIG_CRYPTO_HASH2=y +-CONFIG_CRYPTO_RNG=m +-CONFIG_CRYPTO_RNG2=y +-CONFIG_CRYPTO_PCOMP2=y +-CONFIG_CRYPTO_MANAGER=y +-CONFIG_CRYPTO_MANAGER2=y +-CONFIG_CRYPTO_MANAGER_DISABLE_TESTS=y +-# CONFIG_CRYPTO_GF128MUL is not set +-# CONFIG_CRYPTO_NULL is not set +-CONFIG_CRYPTO_WORKQUEUE=y +-# CONFIG_CRYPTO_CRYPTD is not set + CONFIG_CRYPTO_AUTHENC=m +-# CONFIG_CRYPTO_TEST is not set +- +-# +-# Authenticated Encryption with Associated Data +-# +-# CONFIG_CRYPTO_CCM is not set +-# CONFIG_CRYPTO_GCM is not set + CONFIG_CRYPTO_SEQIV=m +- +-# +-# Block modes +-# + CONFIG_CRYPTO_CBC=y +-# CONFIG_CRYPTO_CTR is not set +-# CONFIG_CRYPTO_CTS is not set +-CONFIG_CRYPTO_ECB=m +-# CONFIG_CRYPTO_LRW is not set +-# CONFIG_CRYPTO_PCBC is not set +-# CONFIG_CRYPTO_XTS is not set +- +-# +-# Hash modes +-# + CONFIG_CRYPTO_HMAC=y + CONFIG_CRYPTO_XCBC=m +-# CONFIG_CRYPTO_VMAC is not set +- +-# +-# Digest +-# +-CONFIG_CRYPTO_CRC32C=y +-# CONFIG_CRYPTO_GHASH is not set +-CONFIG_CRYPTO_MD4=m + CONFIG_CRYPTO_MD5=y +-CONFIG_CRYPTO_MICHAEL_MIC=m +-# CONFIG_CRYPTO_RMD128 is not set +-# CONFIG_CRYPTO_RMD160 is not set +-# CONFIG_CRYPTO_RMD256 is not set +-# CONFIG_CRYPTO_RMD320 is not set + CONFIG_CRYPTO_SHA1=y + CONFIG_CRYPTO_SHA256=m + CONFIG_CRYPTO_SHA512=m + CONFIG_CRYPTO_TGR192=m + CONFIG_CRYPTO_WP512=m +- +-# +-# Ciphers +-# +-# CONFIG_CRYPTO_AES is not set +-# CONFIG_CRYPTO_ANUBIS is not set +-CONFIG_CRYPTO_ARC4=m +-# CONFIG_CRYPTO_BLOWFISH is not set +-# CONFIG_CRYPTO_CAMELLIA is not set + CONFIG_CRYPTO_CAST5=m +-# CONFIG_CRYPTO_CAST6 is not set + CONFIG_CRYPTO_DES=y +-# CONFIG_CRYPTO_FCRYPT is not set +-# CONFIG_CRYPTO_KHAZAD is not set +-# CONFIG_CRYPTO_SALSA20 is not set +-# CONFIG_CRYPTO_SEED is not set +-# CONFIG_CRYPTO_SERPENT is not set +-# CONFIG_CRYPTO_TEA is not set +-# CONFIG_CRYPTO_TWOFISH is not set +- +-# +-# Compression +-# + CONFIG_CRYPTO_DEFLATE=m +-# CONFIG_CRYPTO_ZLIB is not set +-# CONFIG_CRYPTO_LZO is not set +- +-# +-# Random Number Generation +-# + # CONFIG_CRYPTO_ANSI_CPRNG is not set +-# CONFIG_CRYPTO_USER_API_HASH is not set +-# CONFIG_CRYPTO_USER_API_SKCIPHER is not set + # CONFIG_CRYPTO_HW is not set +-# CONFIG_BINARY_PRINTF is not set +- +-# +-# Library routines +-# +-CONFIG_BITREVERSE=y +-CONFIG_CRC_CCITT=m +-CONFIG_CRC16=y +-# CONFIG_CRC_T10DIF is not set + CONFIG_CRC_ITU_T=y +-CONFIG_CRC32=y +-# CONFIG_CRC7 is not set + CONFIG_LIBCRC32C=y +-# CONFIG_CRC8 is not set +-CONFIG_ZLIB_INFLATE=m +-CONFIG_ZLIB_DEFLATE=m +-# CONFIG_XZ_DEC is not set +-# CONFIG_XZ_DEC_BCJ is not set +-CONFIG_HAS_IOMEM=y +-CONFIG_HAS_IOPORT=y +-CONFIG_HAS_DMA=y +-CONFIG_NLATTR=y +-CONFIG_GENERIC_ATOMIC64=y +-# CONFIG_AVERAGE is not set +-# CONFIG_CORDIC is not set +--- /dev/null ++++ b/arch/arm/configs/bcmrpi_defconfig +@@ -0,0 +1,530 @@ ++CONFIG_EXPERIMENTAL=y ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_FHANDLE=y ++CONFIG_AUDIT=y ++CONFIG_IKCONFIG=y ++CONFIG_IKCONFIG_PROC=y ++CONFIG_CGROUP_FREEZER=y ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_CPUACCT=y ++CONFIG_RESOURCE_COUNTERS=y ++CONFIG_BLK_CGROUP=y ++CONFIG_NAMESPACES=y ++CONFIG_SCHED_AUTOGROUP=y ++CONFIG_EMBEDDED=y ++# CONFIG_COMPAT_BRK is not set ++CONFIG_SLAB=y ++CONFIG_PROFILING=y ++CONFIG_OPROFILE=m ++CONFIG_KPROBES=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++# CONFIG_BLK_DEV_BSG is not set ++CONFIG_BLK_DEV_THROTTLING=y ++CONFIG_CFQ_GROUP_IOSCHED=y ++CONFIG_ARCH_BCM2708=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_AEABI=y ++CONFIG_SECCOMP=y ++CONFIG_CC_STACKPROTECTOR=y ++CONFIG_ZBOOT_ROM_TEXT=0x0 ++CONFIG_ZBOOT_ROM_BSS=0x0 ++CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext3 rootwait" ++CONFIG_KEXEC=y ++CONFIG_CPU_IDLE=y ++CONFIG_VFP=y ++CONFIG_BINFMT_MISC=m ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_XFRM_USER=y ++CONFIG_NET_KEY=m ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_RARP=y ++CONFIG_SYN_COOKIES=y ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++# CONFIG_INET_DIAG is not set ++# CONFIG_IPV6 is not set ++CONFIG_NET_PKTGEN=m ++CONFIG_IRDA=m ++CONFIG_IRLAN=m ++CONFIG_IRCOMM=m ++CONFIG_IRDA_ULTRA=y ++CONFIG_IRDA_CACHE_LAST_LSAP=y ++CONFIG_IRDA_FAST_RR=y ++CONFIG_IRTTY_SIR=m ++CONFIG_KINGSUN_DONGLE=m ++CONFIG_KSDAZZLE_DONGLE=m ++CONFIG_KS959_DONGLE=m ++CONFIG_USB_IRDA=m ++CONFIG_SIGMATEL_FIR=m ++CONFIG_MCS_FIR=m ++CONFIG_BT=m ++CONFIG_BT_L2CAP=y ++CONFIG_BT_SCO=y ++CONFIG_BT_RFCOMM=m ++CONFIG_BT_RFCOMM_TTY=y ++CONFIG_BT_BNEP=m ++CONFIG_BT_BNEP_MC_FILTER=y ++CONFIG_BT_BNEP_PROTO_FILTER=y ++CONFIG_BT_HIDP=m ++CONFIG_BT_HCIBTUSB=m ++CONFIG_BT_HCIBCM203X=m ++CONFIG_BT_HCIBPA10X=m ++CONFIG_BT_HCIBFUSB=m ++CONFIG_BT_HCIVHCI=m ++CONFIG_BT_MRVL=m ++CONFIG_BT_MRVL_SDIO=m ++CONFIG_BT_ATH3K=m ++CONFIG_CFG80211=m ++CONFIG_MAC80211=m ++CONFIG_MAC80211_RC_PID=y ++CONFIG_MAC80211_MESH=y ++CONFIG_WIMAX=m ++CONFIG_NET_9P=m ++CONFIG_NFC=m ++CONFIG_NFC_PN533=m ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_CRYPTOLOOP=m ++CONFIG_BLK_DEV_NBD=m ++CONFIG_BLK_DEV_RAM=y ++CONFIG_CDROM_PKTCDVD=m ++CONFIG_MISC_DEVICES=y ++CONFIG_SCSI=y ++# CONFIG_SCSI_PROC_FS is not set ++CONFIG_BLK_DEV_SD=y ++CONFIG_BLK_DEV_SR=m ++CONFIG_SCSI_MULTI_LUN=y ++# CONFIG_SCSI_LOWLEVEL is not set ++CONFIG_MD=y ++CONFIG_NETDEVICES=y ++CONFIG_TUN=m ++CONFIG_PHYLIB=m ++CONFIG_MDIO_BITBANG=m ++CONFIG_NET_ETHERNET=y ++# CONFIG_NETDEV_1000 is not set ++# CONFIG_NETDEV_10000 is not set ++CONFIG_LIBERTAS_THINFIRM=m ++CONFIG_LIBERTAS_THINFIRM_USB=m ++CONFIG_AT76C50X_USB=m ++CONFIG_USB_ZD1201=m ++CONFIG_USB_NET_RNDIS_WLAN=m ++CONFIG_RTL8187=m ++CONFIG_MAC80211_HWSIM=m ++CONFIG_ATH_COMMON=m ++CONFIG_ATH9K=m ++CONFIG_ATH9K_HTC=m ++CONFIG_CARL9170=m ++CONFIG_B43=m ++CONFIG_B43LEGACY=m ++CONFIG_HOSTAP=m ++CONFIG_IWM=m ++CONFIG_LIBERTAS=m ++CONFIG_LIBERTAS_USB=m ++CONFIG_LIBERTAS_SDIO=m ++CONFIG_P54_COMMON=m ++CONFIG_P54_USB=m ++CONFIG_RT2X00=m ++CONFIG_RT2500USB=m ++CONFIG_RT73USB=m ++CONFIG_RT2800USB=m ++CONFIG_RT2800USB_RT53XX=y ++CONFIG_RTL8192CU=m ++CONFIG_WL1251=m ++CONFIG_WL12XX_MENU=m ++CONFIG_ZD1211RW=m ++CONFIG_MWIFIEX=m ++CONFIG_MWIFIEX_SDIO=m ++CONFIG_WIMAX_I2400M_USB=m ++CONFIG_USB_CATC=m ++CONFIG_USB_KAWETH=m ++CONFIG_USB_PEGASUS=m ++CONFIG_USB_RTL8150=m ++CONFIG_USB_USBNET=y ++CONFIG_USB_NET_AX8817X=m ++CONFIG_USB_NET_CDCETHER=m ++CONFIG_USB_NET_CDC_EEM=m ++CONFIG_USB_NET_DM9601=m ++CONFIG_USB_NET_SMSC75XX=m ++CONFIG_USB_NET_SMSC95XX=y ++CONFIG_USB_NET_GL620A=m ++CONFIG_USB_NET_NET1080=m ++CONFIG_USB_NET_PLUSB=m ++CONFIG_USB_NET_MCS7830=m ++CONFIG_USB_NET_CDC_SUBSET=m ++CONFIG_USB_ALI_M5632=y ++CONFIG_USB_AN2720=y ++CONFIG_USB_KC2190=y ++# CONFIG_USB_NET_ZAURUS is not set ++CONFIG_USB_NET_CX82310_ETH=m ++CONFIG_USB_NET_KALMIA=m ++CONFIG_USB_NET_INT51X1=m ++CONFIG_USB_IPHETH=m ++CONFIG_USB_SIERRA_NET=m ++CONFIG_USB_VL600=m ++CONFIG_PPP=m ++CONFIG_PPP_ASYNC=m ++CONFIG_PPP_SYNC_TTY=m ++CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_BSDCOMP=m ++CONFIG_SLIP=m ++CONFIG_SLIP_COMPRESSED=y ++CONFIG_NETCONSOLE=m ++CONFIG_INPUT_POLLDEV=m ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++CONFIG_INPUT_JOYDEV=m ++CONFIG_INPUT_EVDEV=m ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_AD714X=m ++CONFIG_INPUT_ATI_REMOTE=m ++CONFIG_INPUT_ATI_REMOTE2=m ++CONFIG_INPUT_KEYSPAN_REMOTE=m ++CONFIG_INPUT_POWERMATE=m ++CONFIG_INPUT_YEALINK=m ++CONFIG_INPUT_CM109=m ++CONFIG_INPUT_UINPUT=m ++CONFIG_INPUT_GPIO_ROTARY_ENCODER=m ++CONFIG_INPUT_ADXL34X=m ++CONFIG_INPUT_CMA3000=m ++CONFIG_SERIO=m ++CONFIG_SERIO_RAW=m ++CONFIG_GAMEPORT=m ++CONFIG_GAMEPORT_NS558=m ++CONFIG_GAMEPORT_L4=m ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_DEVKMEM is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_HW_RANDOM is not set ++CONFIG_RAW_DRIVER=y ++CONFIG_GPIO_SYSFS=y ++# CONFIG_HWMON is not set ++CONFIG_WATCHDOG=y ++CONFIG_BCM2708_WDT=m ++# CONFIG_MFD_SUPPORT is not set ++CONFIG_FB=y ++CONFIG_FB_BCM2708=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++CONFIG_LOGO=y ++# CONFIG_LOGO_LINUX_MONO is not set ++# CONFIG_LOGO_LINUX_VGA16 is not set ++CONFIG_SOUND=y ++CONFIG_SND=m ++CONFIG_SND_SEQUENCER=m ++CONFIG_SND_SEQ_DUMMY=m ++CONFIG_SND_MIXER_OSS=m ++CONFIG_SND_PCM_OSS=m ++CONFIG_SND_SEQUENCER_OSS=y ++CONFIG_SND_HRTIMER=m ++CONFIG_SND_DUMMY=m ++CONFIG_SND_ALOOP=m ++CONFIG_SND_VIRMIDI=m ++CONFIG_SND_MTPAV=m ++CONFIG_SND_SERIAL_U16550=m ++CONFIG_SND_MPU401=m ++CONFIG_SND_BCM2835=m ++CONFIG_SND_USB_AUDIO=m ++CONFIG_SND_USB_UA101=m ++CONFIG_SND_USB_CAIAQ=m ++CONFIG_SND_USB_6FIRE=m ++CONFIG_SOUND_PRIME=m ++CONFIG_HID_PID=y ++CONFIG_USB_HIDDEV=y ++CONFIG_HID_A4TECH=m ++CONFIG_HID_ACRUX=m ++CONFIG_HID_APPLE=m ++CONFIG_HID_BELKIN=m ++CONFIG_HID_CHERRY=m ++CONFIG_HID_CHICONY=m ++CONFIG_HID_CYPRESS=m ++CONFIG_HID_DRAGONRISE=m ++CONFIG_HID_EMS_FF=m ++CONFIG_HID_ELECOM=m ++CONFIG_HID_EZKEY=m ++CONFIG_HID_HOLTEK=m ++CONFIG_HID_KEYTOUCH=m ++CONFIG_HID_KYE=m ++CONFIG_HID_UCLOGIC=m ++CONFIG_HID_WALTOP=m ++CONFIG_HID_GYRATION=m ++CONFIG_HID_TWINHAN=m ++CONFIG_HID_KENSINGTON=m ++CONFIG_HID_LCPOWER=m ++CONFIG_HID_LOGITECH=m ++CONFIG_HID_MAGICMOUSE=m ++CONFIG_HID_MICROSOFT=m ++CONFIG_HID_MONTEREY=m ++CONFIG_HID_MULTITOUCH=m ++CONFIG_HID_NTRIG=m ++CONFIG_HID_ORTEK=m ++CONFIG_HID_PANTHERLORD=m ++CONFIG_HID_PETALYNX=m ++CONFIG_HID_PICOLCD=m ++CONFIG_HID_QUANTA=m ++CONFIG_HID_ROCCAT=m ++CONFIG_HID_SAMSUNG=m ++CONFIG_HID_SONY=m ++CONFIG_HID_SPEEDLINK=m ++CONFIG_HID_SUNPLUS=m ++CONFIG_HID_GREENASIA=m ++CONFIG_HID_SMARTJOYPLUS=m ++CONFIG_HID_TOPSEED=m ++CONFIG_HID_THRUSTMASTER=m ++CONFIG_HID_WACOM=m ++CONFIG_HID_WIIMOTE=m ++CONFIG_HID_ZEROPLUS=m ++CONFIG_HID_ZYDACRON=m ++CONFIG_USB=y ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_MON=m ++CONFIG_USB_DWCOTG=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_STORAGE_REALTEK=m ++CONFIG_USB_STORAGE_DATAFAB=m ++CONFIG_USB_STORAGE_FREECOM=m ++CONFIG_USB_STORAGE_ISD200=m ++CONFIG_USB_STORAGE_USBAT=m ++CONFIG_USB_STORAGE_SDDR09=m ++CONFIG_USB_STORAGE_SDDR55=m ++CONFIG_USB_STORAGE_JUMPSHOT=m ++CONFIG_USB_STORAGE_ALAUDA=m ++CONFIG_USB_STORAGE_ONETOUCH=m ++CONFIG_USB_STORAGE_KARMA=m ++CONFIG_USB_STORAGE_CYPRESS_ATACB=m ++CONFIG_USB_STORAGE_ENE_UB6250=m ++CONFIG_USB_UAS=y ++CONFIG_USB_LIBUSUAL=y ++CONFIG_USB_MDC800=m ++CONFIG_USB_MICROTEK=m ++CONFIG_USB_SERIAL=m ++CONFIG_USB_SERIAL_GENERIC=y ++CONFIG_USB_SERIAL_AIRCABLE=m ++CONFIG_USB_SERIAL_ARK3116=m ++CONFIG_USB_SERIAL_BELKIN=m ++CONFIG_USB_SERIAL_CH341=m ++CONFIG_USB_SERIAL_WHITEHEAT=m ++CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m ++CONFIG_USB_SERIAL_CP210X=m ++CONFIG_USB_SERIAL_CYPRESS_M8=m ++CONFIG_USB_SERIAL_EMPEG=m ++CONFIG_USB_SERIAL_FTDI_SIO=m ++CONFIG_USB_SERIAL_FUNSOFT=m ++CONFIG_USB_SERIAL_VISOR=m ++CONFIG_USB_SERIAL_IPAQ=m ++CONFIG_USB_SERIAL_IR=m ++CONFIG_USB_SERIAL_EDGEPORT=m ++CONFIG_USB_SERIAL_EDGEPORT_TI=m ++CONFIG_USB_SERIAL_GARMIN=m ++CONFIG_USB_SERIAL_IPW=m ++CONFIG_USB_SERIAL_IUU=m ++CONFIG_USB_SERIAL_KEYSPAN_PDA=m ++CONFIG_USB_SERIAL_KEYSPAN=m ++CONFIG_USB_SERIAL_KLSI=m ++CONFIG_USB_SERIAL_KOBIL_SCT=m ++CONFIG_USB_SERIAL_MCT_U232=m ++CONFIG_USB_SERIAL_MOS7720=m ++CONFIG_USB_SERIAL_MOS7840=m ++CONFIG_USB_SERIAL_MOTOROLA=m ++CONFIG_USB_SERIAL_NAVMAN=m ++CONFIG_USB_SERIAL_PL2303=m ++CONFIG_USB_SERIAL_OTI6858=m ++CONFIG_USB_SERIAL_QCAUX=m ++CONFIG_USB_SERIAL_QUALCOMM=m ++CONFIG_USB_SERIAL_SPCP8X5=m ++CONFIG_USB_SERIAL_HP4X=m ++CONFIG_USB_SERIAL_SAFE=m ++CONFIG_USB_SERIAL_SIEMENS_MPI=m ++CONFIG_USB_SERIAL_SIERRAWIRELESS=m ++CONFIG_USB_SERIAL_SYMBOL=m ++CONFIG_USB_SERIAL_TI=m ++CONFIG_USB_SERIAL_CYBERJACK=m ++CONFIG_USB_SERIAL_XIRCOM=m ++CONFIG_USB_SERIAL_OPTION=m ++CONFIG_USB_SERIAL_OMNINET=m ++CONFIG_USB_SERIAL_OPTICON=m ++CONFIG_USB_SERIAL_VIVOPAY_SERIAL=m ++CONFIG_USB_SERIAL_ZIO=m ++CONFIG_USB_SERIAL_SSU100=m ++CONFIG_USB_SERIAL_DEBUG=m ++CONFIG_USB_EMI62=m ++CONFIG_USB_EMI26=m ++CONFIG_USB_ADUTUX=m ++CONFIG_USB_SEVSEG=m ++CONFIG_USB_RIO500=m ++CONFIG_USB_LEGOTOWER=m ++CONFIG_USB_LCD=m ++CONFIG_USB_LED=m ++CONFIG_USB_CYPRESS_CY7C63=m ++CONFIG_USB_CYTHERM=m ++CONFIG_USB_IDMOUSE=m ++CONFIG_USB_FTDI_ELAN=m ++CONFIG_USB_APPLEDISPLAY=m ++CONFIG_USB_LD=m ++CONFIG_USB_TRANCEVIBRATOR=m ++CONFIG_USB_IOWARRIOR=m ++CONFIG_USB_TEST=m ++CONFIG_USB_ISIGHTFW=m ++CONFIG_USB_YUREX=m ++CONFIG_MMC=y ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SDHCI_BCM2708=y ++CONFIG_MMC_SDHCI_BCM2708_DMA=y ++CONFIG_LEDS_GPIO=y ++CONFIG_LEDS_TRIGGER_TIMER=m ++CONFIG_LEDS_TRIGGER_HEARTBEAT=m ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=m ++CONFIG_UIO=m ++CONFIG_UIO_PDRV=m ++CONFIG_UIO_PDRV_GENIRQ=m ++# CONFIG_IOMMU_SUPPORT is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_REISERFS_FS=m ++CONFIG_REISERFS_FS_XATTR=y ++CONFIG_REISERFS_FS_POSIX_ACL=y ++CONFIG_REISERFS_FS_SECURITY=y ++CONFIG_JFS_FS=m ++CONFIG_JFS_POSIX_ACL=y ++CONFIG_JFS_SECURITY=y ++CONFIG_JFS_STATISTICS=y ++CONFIG_XFS_FS=m ++CONFIG_XFS_QUOTA=y ++CONFIG_XFS_POSIX_ACL=y ++CONFIG_XFS_RT=y ++CONFIG_GFS2_FS=m ++CONFIG_OCFS2_FS=m ++CONFIG_BTRFS_FS=m ++CONFIG_BTRFS_FS_POSIX_ACL=y ++CONFIG_NILFS2_FS=m ++CONFIG_FANOTIFY=y ++CONFIG_AUTOFS4_FS=y ++CONFIG_FUSE_FS=m ++CONFIG_CUSE=m ++CONFIG_FSCACHE=y ++CONFIG_FSCACHE_STATS=y ++CONFIG_FSCACHE_HISTOGRAM=y ++CONFIG_CACHEFILES=y ++CONFIG_ISO9660_FS=m ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_UDF_FS=m ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_IOCHARSET="ascii" ++CONFIG_NTFS_FS=m ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_CONFIGFS_FS=y ++CONFIG_SQUASHFS=m ++CONFIG_SQUASHFS_XATTR=y ++CONFIG_SQUASHFS_LZO=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_ROOT_NFS=y ++CONFIG_NFS_FSCACHE=y ++CONFIG_CIFS=m ++CONFIG_CIFS_WEAK_PW_HASH=y ++CONFIG_CIFS_XATTR=y ++CONFIG_CIFS_POSIX=y ++CONFIG_9P_FS=m ++CONFIG_9P_FS_POSIX_ACL=y ++CONFIG_PARTITION_ADVANCED=y ++CONFIG_MAC_PARTITION=y ++CONFIG_EFI_PARTITION=y ++CONFIG_NLS_DEFAULT="utf8" ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_CODEPAGE_737=m ++CONFIG_NLS_CODEPAGE_775=m ++CONFIG_NLS_CODEPAGE_850=m ++CONFIG_NLS_CODEPAGE_852=m ++CONFIG_NLS_CODEPAGE_855=m ++CONFIG_NLS_CODEPAGE_857=m ++CONFIG_NLS_CODEPAGE_860=m ++CONFIG_NLS_CODEPAGE_861=m ++CONFIG_NLS_CODEPAGE_862=m ++CONFIG_NLS_CODEPAGE_863=m ++CONFIG_NLS_CODEPAGE_864=m ++CONFIG_NLS_CODEPAGE_865=m ++CONFIG_NLS_CODEPAGE_866=m ++CONFIG_NLS_CODEPAGE_869=m ++CONFIG_NLS_CODEPAGE_936=m ++CONFIG_NLS_CODEPAGE_950=m ++CONFIG_NLS_CODEPAGE_932=m ++CONFIG_NLS_CODEPAGE_949=m ++CONFIG_NLS_CODEPAGE_874=m ++CONFIG_NLS_ISO8859_8=m ++CONFIG_NLS_CODEPAGE_1250=m ++CONFIG_NLS_CODEPAGE_1251=m ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=m ++CONFIG_NLS_ISO8859_2=m ++CONFIG_NLS_ISO8859_3=m ++CONFIG_NLS_ISO8859_4=m ++CONFIG_NLS_ISO8859_5=m ++CONFIG_NLS_ISO8859_6=m ++CONFIG_NLS_ISO8859_7=m ++CONFIG_NLS_ISO8859_9=m ++CONFIG_NLS_ISO8859_13=m ++CONFIG_NLS_ISO8859_14=m ++CONFIG_NLS_ISO8859_15=m ++CONFIG_NLS_KOI8_R=m ++CONFIG_NLS_KOI8_U=m ++CONFIG_NLS_UTF8=m ++CONFIG_PRINTK_TIME=y ++CONFIG_DETECT_HUNG_TASK=y ++CONFIG_TIMER_STATS=y ++CONFIG_DEBUG_STACK_USAGE=y ++CONFIG_DEBUG_INFO=y ++CONFIG_DEBUG_MEMORY_INIT=y ++CONFIG_BOOT_PRINTK_DELAY=y ++CONFIG_LATENCYTOP=y ++CONFIG_SYSCTL_SYSCALL_CHECK=y ++CONFIG_IRQSOFF_TRACER=y ++CONFIG_SCHED_TRACER=y ++CONFIG_STACK_TRACER=y ++CONFIG_BLK_DEV_IO_TRACE=y ++CONFIG_FUNCTION_PROFILER=y ++CONFIG_KGDB=y ++CONFIG_KGDB_KDB=y ++CONFIG_KDB_KEYBOARD=y ++CONFIG_STRICT_DEVMEM=y ++CONFIG_CRYPTO_AUTHENC=m ++CONFIG_CRYPTO_SEQIV=m ++CONFIG_CRYPTO_CBC=y ++CONFIG_CRYPTO_HMAC=y ++CONFIG_CRYPTO_XCBC=m ++CONFIG_CRYPTO_MD5=y ++CONFIG_CRYPTO_SHA1=y ++CONFIG_CRYPTO_SHA256=m ++CONFIG_CRYPTO_SHA512=m ++CONFIG_CRYPTO_TGR192=m ++CONFIG_CRYPTO_WP512=m ++CONFIG_CRYPTO_CAST5=m ++CONFIG_CRYPTO_DES=y ++CONFIG_CRYPTO_DEFLATE=m ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_HW is not set ++CONFIG_CRC_ITU_T=y ++CONFIG_LIBCRC32C=y +--- /dev/null ++++ b/arch/arm/configs/bcmrpi_emergency_defconfig +@@ -0,0 +1,532 @@ ++CONFIG_EXPERIMENTAL=y ++# CONFIG_LOCALVERSION_AUTO is not set ++CONFIG_SYSVIPC=y ++CONFIG_POSIX_MQUEUE=y ++CONFIG_BSD_PROCESS_ACCT=y ++CONFIG_BSD_PROCESS_ACCT_V3=y ++CONFIG_FHANDLE=y ++CONFIG_AUDIT=y ++CONFIG_IKCONFIG=y ++CONFIG_IKCONFIG_PROC=y ++CONFIG_BLK_DEV_INITRD=y ++CONFIG_INITRAMFS_SOURCE="../target_fs" ++CONFIG_CGROUP_FREEZER=y ++CONFIG_CGROUP_DEVICE=y ++CONFIG_CGROUP_CPUACCT=y ++CONFIG_RESOURCE_COUNTERS=y ++CONFIG_BLK_CGROUP=y ++CONFIG_NAMESPACES=y ++CONFIG_SCHED_AUTOGROUP=y ++CONFIG_EMBEDDED=y ++# CONFIG_COMPAT_BRK is not set ++CONFIG_SLAB=y ++CONFIG_PROFILING=y ++CONFIG_OPROFILE=m ++CONFIG_KPROBES=y ++CONFIG_MODULES=y ++CONFIG_MODULE_UNLOAD=y ++CONFIG_MODVERSIONS=y ++CONFIG_MODULE_SRCVERSION_ALL=y ++# CONFIG_BLK_DEV_BSG is not set ++CONFIG_BLK_DEV_THROTTLING=y ++CONFIG_CFQ_GROUP_IOSCHED=y ++CONFIG_ARCH_BCM2708=y ++CONFIG_NO_HZ=y ++CONFIG_HIGH_RES_TIMERS=y ++CONFIG_AEABI=y ++CONFIG_SECCOMP=y ++CONFIG_CC_STACKPROTECTOR=y ++CONFIG_ZBOOT_ROM_TEXT=0x0 ++CONFIG_ZBOOT_ROM_BSS=0x0 ++CONFIG_CMDLINE="dwc_otg.lpm_enable=0 console=ttyAMA0,115200 kgdboc=ttyAMA0,115200 root=/dev/mmcblk0p2 rootfstype=ext3 rootwait" ++CONFIG_KEXEC=y ++CONFIG_CPU_IDLE=y ++CONFIG_VFP=y ++CONFIG_BINFMT_MISC=m ++CONFIG_NET=y ++CONFIG_PACKET=y ++CONFIG_UNIX=y ++CONFIG_XFRM_USER=y ++CONFIG_NET_KEY=m ++CONFIG_INET=y ++CONFIG_IP_MULTICAST=y ++CONFIG_IP_PNP=y ++CONFIG_IP_PNP_DHCP=y ++CONFIG_IP_PNP_RARP=y ++CONFIG_SYN_COOKIES=y ++# CONFIG_INET_XFRM_MODE_TRANSPORT is not set ++# CONFIG_INET_XFRM_MODE_TUNNEL is not set ++# CONFIG_INET_XFRM_MODE_BEET is not set ++# CONFIG_INET_LRO is not set ++# CONFIG_INET_DIAG is not set ++# CONFIG_IPV6 is not set ++CONFIG_NET_PKTGEN=m ++CONFIG_IRDA=m ++CONFIG_IRLAN=m ++CONFIG_IRCOMM=m ++CONFIG_IRDA_ULTRA=y ++CONFIG_IRDA_CACHE_LAST_LSAP=y ++CONFIG_IRDA_FAST_RR=y ++CONFIG_IRTTY_SIR=m ++CONFIG_KINGSUN_DONGLE=m ++CONFIG_KSDAZZLE_DONGLE=m ++CONFIG_KS959_DONGLE=m ++CONFIG_USB_IRDA=m ++CONFIG_SIGMATEL_FIR=m ++CONFIG_MCS_FIR=m ++CONFIG_BT=m ++CONFIG_BT_L2CAP=y ++CONFIG_BT_SCO=y ++CONFIG_BT_RFCOMM=m ++CONFIG_BT_RFCOMM_TTY=y ++CONFIG_BT_BNEP=m ++CONFIG_BT_BNEP_MC_FILTER=y ++CONFIG_BT_BNEP_PROTO_FILTER=y ++CONFIG_BT_HIDP=m ++CONFIG_BT_HCIBTUSB=m ++CONFIG_BT_HCIBCM203X=m ++CONFIG_BT_HCIBPA10X=m ++CONFIG_BT_HCIBFUSB=m ++CONFIG_BT_HCIVHCI=m ++CONFIG_BT_MRVL=m ++CONFIG_BT_MRVL_SDIO=m ++CONFIG_BT_ATH3K=m ++CONFIG_CFG80211=m ++CONFIG_MAC80211=m ++CONFIG_MAC80211_RC_PID=y ++CONFIG_MAC80211_MESH=y ++CONFIG_WIMAX=m ++CONFIG_NET_9P=m ++CONFIG_NFC=m ++CONFIG_NFC_PN533=m ++CONFIG_UEVENT_HELPER_PATH="/sbin/hotplug" ++CONFIG_BLK_DEV_LOOP=y ++CONFIG_BLK_DEV_CRYPTOLOOP=m ++CONFIG_BLK_DEV_NBD=m ++CONFIG_BLK_DEV_RAM=y ++CONFIG_CDROM_PKTCDVD=m ++CONFIG_MISC_DEVICES=y ++CONFIG_SCSI=y ++# CONFIG_SCSI_PROC_FS is not set ++CONFIG_BLK_DEV_SD=y ++CONFIG_BLK_DEV_SR=m ++CONFIG_SCSI_MULTI_LUN=y ++# CONFIG_SCSI_LOWLEVEL is not set ++CONFIG_MD=y ++CONFIG_NETDEVICES=y ++CONFIG_TUN=m ++CONFIG_PHYLIB=m ++CONFIG_MDIO_BITBANG=m ++CONFIG_NET_ETHERNET=y ++# CONFIG_NETDEV_1000 is not set ++# CONFIG_NETDEV_10000 is not set ++CONFIG_LIBERTAS_THINFIRM=m ++CONFIG_LIBERTAS_THINFIRM_USB=m ++CONFIG_AT76C50X_USB=m ++CONFIG_USB_ZD1201=m ++CONFIG_USB_NET_RNDIS_WLAN=m ++CONFIG_RTL8187=m ++CONFIG_MAC80211_HWSIM=m ++CONFIG_ATH_COMMON=m ++CONFIG_ATH9K=m ++CONFIG_ATH9K_HTC=m ++CONFIG_CARL9170=m ++CONFIG_B43=m ++CONFIG_B43LEGACY=m ++CONFIG_HOSTAP=m ++CONFIG_IWM=m ++CONFIG_LIBERTAS=m ++CONFIG_LIBERTAS_USB=m ++CONFIG_LIBERTAS_SDIO=m ++CONFIG_P54_COMMON=m ++CONFIG_P54_USB=m ++CONFIG_RT2X00=m ++CONFIG_RT2500USB=m ++CONFIG_RT73USB=m ++CONFIG_RT2800USB=m ++CONFIG_RT2800USB_RT53XX=y ++CONFIG_RTL8192CU=m ++CONFIG_WL1251=m ++CONFIG_WL12XX_MENU=m ++CONFIG_ZD1211RW=m ++CONFIG_MWIFIEX=m ++CONFIG_MWIFIEX_SDIO=m ++CONFIG_WIMAX_I2400M_USB=m ++CONFIG_USB_CATC=m ++CONFIG_USB_KAWETH=m ++CONFIG_USB_PEGASUS=m ++CONFIG_USB_RTL8150=m ++CONFIG_USB_USBNET=y ++CONFIG_USB_NET_AX8817X=m ++CONFIG_USB_NET_CDCETHER=m ++CONFIG_USB_NET_CDC_EEM=m ++CONFIG_USB_NET_DM9601=m ++CONFIG_USB_NET_SMSC75XX=m ++CONFIG_USB_NET_SMSC95XX=y ++CONFIG_USB_NET_GL620A=m ++CONFIG_USB_NET_NET1080=m ++CONFIG_USB_NET_PLUSB=m ++CONFIG_USB_NET_MCS7830=m ++CONFIG_USB_NET_CDC_SUBSET=m ++CONFIG_USB_ALI_M5632=y ++CONFIG_USB_AN2720=y ++CONFIG_USB_KC2190=y ++# CONFIG_USB_NET_ZAURUS is not set ++CONFIG_USB_NET_CX82310_ETH=m ++CONFIG_USB_NET_KALMIA=m ++CONFIG_USB_NET_INT51X1=m ++CONFIG_USB_IPHETH=m ++CONFIG_USB_SIERRA_NET=m ++CONFIG_USB_VL600=m ++CONFIG_PPP=m ++CONFIG_PPP_ASYNC=m ++CONFIG_PPP_SYNC_TTY=m ++CONFIG_PPP_DEFLATE=m ++CONFIG_PPP_BSDCOMP=m ++CONFIG_SLIP=m ++CONFIG_SLIP_COMPRESSED=y ++CONFIG_NETCONSOLE=m ++CONFIG_INPUT_POLLDEV=m ++# CONFIG_INPUT_MOUSEDEV_PSAUX is not set ++CONFIG_INPUT_JOYDEV=m ++CONFIG_INPUT_EVDEV=m ++# CONFIG_INPUT_KEYBOARD is not set ++# CONFIG_INPUT_MOUSE is not set ++CONFIG_INPUT_MISC=y ++CONFIG_INPUT_AD714X=m ++CONFIG_INPUT_ATI_REMOTE=m ++CONFIG_INPUT_ATI_REMOTE2=m ++CONFIG_INPUT_KEYSPAN_REMOTE=m ++CONFIG_INPUT_POWERMATE=m ++CONFIG_INPUT_YEALINK=m ++CONFIG_INPUT_CM109=m ++CONFIG_INPUT_UINPUT=m ++CONFIG_INPUT_GPIO_ROTARY_ENCODER=m ++CONFIG_INPUT_ADXL34X=m ++CONFIG_INPUT_CMA3000=m ++CONFIG_SERIO=m ++CONFIG_SERIO_RAW=m ++CONFIG_GAMEPORT=m ++CONFIG_GAMEPORT_NS558=m ++CONFIG_GAMEPORT_L4=m ++CONFIG_VT_HW_CONSOLE_BINDING=y ++# CONFIG_LEGACY_PTYS is not set ++# CONFIG_DEVKMEM is not set ++CONFIG_SERIAL_AMBA_PL011=y ++CONFIG_SERIAL_AMBA_PL011_CONSOLE=y ++# CONFIG_HW_RANDOM is not set ++CONFIG_RAW_DRIVER=y ++CONFIG_GPIO_SYSFS=y ++# CONFIG_HWMON is not set ++CONFIG_WATCHDOG=y ++CONFIG_BCM2708_WDT=m ++# CONFIG_MFD_SUPPORT is not set ++CONFIG_FB=y ++CONFIG_FB_BCM2708=y ++CONFIG_FRAMEBUFFER_CONSOLE=y ++CONFIG_LOGO=y ++# CONFIG_LOGO_LINUX_MONO is not set ++# CONFIG_LOGO_LINUX_VGA16 is not set ++CONFIG_SOUND=y ++CONFIG_SND=m ++CONFIG_SND_SEQUENCER=m ++CONFIG_SND_SEQ_DUMMY=m ++CONFIG_SND_MIXER_OSS=m ++CONFIG_SND_PCM_OSS=m ++CONFIG_SND_SEQUENCER_OSS=y ++CONFIG_SND_HRTIMER=m ++CONFIG_SND_DUMMY=m ++CONFIG_SND_ALOOP=m ++CONFIG_SND_VIRMIDI=m ++CONFIG_SND_MTPAV=m ++CONFIG_SND_SERIAL_U16550=m ++CONFIG_SND_MPU401=m ++CONFIG_SND_BCM2835=m ++CONFIG_SND_USB_AUDIO=m ++CONFIG_SND_USB_UA101=m ++CONFIG_SND_USB_CAIAQ=m ++CONFIG_SND_USB_6FIRE=m ++CONFIG_SOUND_PRIME=m ++CONFIG_HID_PID=y ++CONFIG_USB_HIDDEV=y ++CONFIG_HID_A4TECH=m ++CONFIG_HID_ACRUX=m ++CONFIG_HID_APPLE=m ++CONFIG_HID_BELKIN=m ++CONFIG_HID_CHERRY=m ++CONFIG_HID_CHICONY=m ++CONFIG_HID_CYPRESS=m ++CONFIG_HID_DRAGONRISE=m ++CONFIG_HID_EMS_FF=m ++CONFIG_HID_ELECOM=m ++CONFIG_HID_EZKEY=m ++CONFIG_HID_HOLTEK=m ++CONFIG_HID_KEYTOUCH=m ++CONFIG_HID_KYE=m ++CONFIG_HID_UCLOGIC=m ++CONFIG_HID_WALTOP=m ++CONFIG_HID_GYRATION=m ++CONFIG_HID_TWINHAN=m ++CONFIG_HID_KENSINGTON=m ++CONFIG_HID_LCPOWER=m ++CONFIG_HID_LOGITECH=m ++CONFIG_HID_MAGICMOUSE=m ++CONFIG_HID_MICROSOFT=m ++CONFIG_HID_MONTEREY=m ++CONFIG_HID_MULTITOUCH=m ++CONFIG_HID_NTRIG=m ++CONFIG_HID_ORTEK=m ++CONFIG_HID_PANTHERLORD=m ++CONFIG_HID_PETALYNX=m ++CONFIG_HID_PICOLCD=m ++CONFIG_HID_QUANTA=m ++CONFIG_HID_ROCCAT=m ++CONFIG_HID_SAMSUNG=m ++CONFIG_HID_SONY=m ++CONFIG_HID_SPEEDLINK=m ++CONFIG_HID_SUNPLUS=m ++CONFIG_HID_GREENASIA=m ++CONFIG_HID_SMARTJOYPLUS=m ++CONFIG_HID_TOPSEED=m ++CONFIG_HID_THRUSTMASTER=m ++CONFIG_HID_WACOM=m ++CONFIG_HID_WIIMOTE=m ++CONFIG_HID_ZEROPLUS=m ++CONFIG_HID_ZYDACRON=m ++CONFIG_USB=y ++CONFIG_USB_ANNOUNCE_NEW_DEVICES=y ++CONFIG_USB_MON=m ++CONFIG_USB_DWCOTG=y ++CONFIG_USB_STORAGE=y ++CONFIG_USB_STORAGE_REALTEK=m ++CONFIG_USB_STORAGE_DATAFAB=m ++CONFIG_USB_STORAGE_FREECOM=m ++CONFIG_USB_STORAGE_ISD200=m ++CONFIG_USB_STORAGE_USBAT=m ++CONFIG_USB_STORAGE_SDDR09=m ++CONFIG_USB_STORAGE_SDDR55=m ++CONFIG_USB_STORAGE_JUMPSHOT=m ++CONFIG_USB_STORAGE_ALAUDA=m ++CONFIG_USB_STORAGE_ONETOUCH=m ++CONFIG_USB_STORAGE_KARMA=m ++CONFIG_USB_STORAGE_CYPRESS_ATACB=m ++CONFIG_USB_STORAGE_ENE_UB6250=m ++CONFIG_USB_UAS=y ++CONFIG_USB_LIBUSUAL=y ++CONFIG_USB_MDC800=m ++CONFIG_USB_MICROTEK=m ++CONFIG_USB_SERIAL=m ++CONFIG_USB_SERIAL_GENERIC=y ++CONFIG_USB_SERIAL_AIRCABLE=m ++CONFIG_USB_SERIAL_ARK3116=m ++CONFIG_USB_SERIAL_BELKIN=m ++CONFIG_USB_SERIAL_CH341=m ++CONFIG_USB_SERIAL_WHITEHEAT=m ++CONFIG_USB_SERIAL_DIGI_ACCELEPORT=m ++CONFIG_USB_SERIAL_CP210X=m ++CONFIG_USB_SERIAL_CYPRESS_M8=m ++CONFIG_USB_SERIAL_EMPEG=m ++CONFIG_USB_SERIAL_FTDI_SIO=m ++CONFIG_USB_SERIAL_FUNSOFT=m ++CONFIG_USB_SERIAL_VISOR=m ++CONFIG_USB_SERIAL_IPAQ=m ++CONFIG_USB_SERIAL_IR=m ++CONFIG_USB_SERIAL_EDGEPORT=m ++CONFIG_USB_SERIAL_EDGEPORT_TI=m ++CONFIG_USB_SERIAL_GARMIN=m ++CONFIG_USB_SERIAL_IPW=m ++CONFIG_USB_SERIAL_IUU=m ++CONFIG_USB_SERIAL_KEYSPAN_PDA=m ++CONFIG_USB_SERIAL_KEYSPAN=m ++CONFIG_USB_SERIAL_KLSI=m ++CONFIG_USB_SERIAL_KOBIL_SCT=m ++CONFIG_USB_SERIAL_MCT_U232=m ++CONFIG_USB_SERIAL_MOS7720=m ++CONFIG_USB_SERIAL_MOS7840=m ++CONFIG_USB_SERIAL_MOTOROLA=m ++CONFIG_USB_SERIAL_NAVMAN=m ++CONFIG_USB_SERIAL_PL2303=m ++CONFIG_USB_SERIAL_OTI6858=m ++CONFIG_USB_SERIAL_QCAUX=m ++CONFIG_USB_SERIAL_QUALCOMM=m ++CONFIG_USB_SERIAL_SPCP8X5=m ++CONFIG_USB_SERIAL_HP4X=m ++CONFIG_USB_SERIAL_SAFE=m ++CONFIG_USB_SERIAL_SIEMENS_MPI=m ++CONFIG_USB_SERIAL_SIERRAWIRELESS=m ++CONFIG_USB_SERIAL_SYMBOL=m ++CONFIG_USB_SERIAL_TI=m ++CONFIG_USB_SERIAL_CYBERJACK=m ++CONFIG_USB_SERIAL_XIRCOM=m ++CONFIG_USB_SERIAL_OPTION=m ++CONFIG_USB_SERIAL_OMNINET=m ++CONFIG_USB_SERIAL_OPTICON=m ++CONFIG_USB_SERIAL_VIVOPAY_SERIAL=m ++CONFIG_USB_SERIAL_ZIO=m ++CONFIG_USB_SERIAL_SSU100=m ++CONFIG_USB_SERIAL_DEBUG=m ++CONFIG_USB_EMI62=m ++CONFIG_USB_EMI26=m ++CONFIG_USB_ADUTUX=m ++CONFIG_USB_SEVSEG=m ++CONFIG_USB_RIO500=m ++CONFIG_USB_LEGOTOWER=m ++CONFIG_USB_LCD=m ++CONFIG_USB_LED=m ++CONFIG_USB_CYPRESS_CY7C63=m ++CONFIG_USB_CYTHERM=m ++CONFIG_USB_IDMOUSE=m ++CONFIG_USB_FTDI_ELAN=m ++CONFIG_USB_APPLEDISPLAY=m ++CONFIG_USB_LD=m ++CONFIG_USB_TRANCEVIBRATOR=m ++CONFIG_USB_IOWARRIOR=m ++CONFIG_USB_TEST=m ++CONFIG_USB_ISIGHTFW=m ++CONFIG_USB_YUREX=m ++CONFIG_MMC=y ++CONFIG_MMC_SDHCI=y ++CONFIG_MMC_SDHCI_PLTFM=y ++CONFIG_MMC_SDHCI_BCM2708=y ++CONFIG_MMC_SDHCI_BCM2708_DMA=y ++CONFIG_LEDS_GPIO=y ++CONFIG_LEDS_TRIGGER_TIMER=m ++CONFIG_LEDS_TRIGGER_HEARTBEAT=m ++CONFIG_LEDS_TRIGGER_DEFAULT_ON=m ++CONFIG_UIO=m ++CONFIG_UIO_PDRV=m ++CONFIG_UIO_PDRV_GENIRQ=m ++# CONFIG_IOMMU_SUPPORT is not set ++CONFIG_EXT4_FS=y ++CONFIG_EXT4_FS_POSIX_ACL=y ++CONFIG_EXT4_FS_SECURITY=y ++CONFIG_REISERFS_FS=m ++CONFIG_REISERFS_FS_XATTR=y ++CONFIG_REISERFS_FS_POSIX_ACL=y ++CONFIG_REISERFS_FS_SECURITY=y ++CONFIG_JFS_FS=m ++CONFIG_JFS_POSIX_ACL=y ++CONFIG_JFS_SECURITY=y ++CONFIG_JFS_STATISTICS=y ++CONFIG_XFS_FS=m ++CONFIG_XFS_QUOTA=y ++CONFIG_XFS_POSIX_ACL=y ++CONFIG_XFS_RT=y ++CONFIG_GFS2_FS=m ++CONFIG_OCFS2_FS=m ++CONFIG_BTRFS_FS=m ++CONFIG_BTRFS_FS_POSIX_ACL=y ++CONFIG_NILFS2_FS=m ++CONFIG_FANOTIFY=y ++CONFIG_AUTOFS4_FS=y ++CONFIG_FUSE_FS=m ++CONFIG_CUSE=m ++CONFIG_FSCACHE=y ++CONFIG_FSCACHE_STATS=y ++CONFIG_FSCACHE_HISTOGRAM=y ++CONFIG_CACHEFILES=y ++CONFIG_ISO9660_FS=m ++CONFIG_JOLIET=y ++CONFIG_ZISOFS=y ++CONFIG_UDF_FS=m ++CONFIG_MSDOS_FS=y ++CONFIG_VFAT_FS=y ++CONFIG_FAT_DEFAULT_IOCHARSET="ascii" ++CONFIG_NTFS_FS=m ++CONFIG_TMPFS=y ++CONFIG_TMPFS_POSIX_ACL=y ++CONFIG_CONFIGFS_FS=y ++CONFIG_SQUASHFS=m ++CONFIG_SQUASHFS_XATTR=y ++CONFIG_SQUASHFS_LZO=y ++CONFIG_SQUASHFS_XZ=y ++CONFIG_NFS_FS=y ++CONFIG_NFS_V3=y ++CONFIG_NFS_V3_ACL=y ++CONFIG_NFS_V4=y ++CONFIG_ROOT_NFS=y ++CONFIG_NFS_FSCACHE=y ++CONFIG_CIFS=m ++CONFIG_CIFS_WEAK_PW_HASH=y ++CONFIG_CIFS_XATTR=y ++CONFIG_CIFS_POSIX=y ++CONFIG_9P_FS=m ++CONFIG_9P_FS_POSIX_ACL=y ++CONFIG_PARTITION_ADVANCED=y ++CONFIG_MAC_PARTITION=y ++CONFIG_EFI_PARTITION=y ++CONFIG_NLS_DEFAULT="utf8" ++CONFIG_NLS_CODEPAGE_437=y ++CONFIG_NLS_CODEPAGE_737=m ++CONFIG_NLS_CODEPAGE_775=m ++CONFIG_NLS_CODEPAGE_850=m ++CONFIG_NLS_CODEPAGE_852=m ++CONFIG_NLS_CODEPAGE_855=m ++CONFIG_NLS_CODEPAGE_857=m ++CONFIG_NLS_CODEPAGE_860=m ++CONFIG_NLS_CODEPAGE_861=m ++CONFIG_NLS_CODEPAGE_862=m ++CONFIG_NLS_CODEPAGE_863=m ++CONFIG_NLS_CODEPAGE_864=m ++CONFIG_NLS_CODEPAGE_865=m ++CONFIG_NLS_CODEPAGE_866=m ++CONFIG_NLS_CODEPAGE_869=m ++CONFIG_NLS_CODEPAGE_936=m ++CONFIG_NLS_CODEPAGE_950=m ++CONFIG_NLS_CODEPAGE_932=m ++CONFIG_NLS_CODEPAGE_949=m ++CONFIG_NLS_CODEPAGE_874=m ++CONFIG_NLS_ISO8859_8=m ++CONFIG_NLS_CODEPAGE_1250=m ++CONFIG_NLS_CODEPAGE_1251=m ++CONFIG_NLS_ASCII=y ++CONFIG_NLS_ISO8859_1=m ++CONFIG_NLS_ISO8859_2=m ++CONFIG_NLS_ISO8859_3=m ++CONFIG_NLS_ISO8859_4=m ++CONFIG_NLS_ISO8859_5=m ++CONFIG_NLS_ISO8859_6=m ++CONFIG_NLS_ISO8859_7=m ++CONFIG_NLS_ISO8859_9=m ++CONFIG_NLS_ISO8859_13=m ++CONFIG_NLS_ISO8859_14=m ++CONFIG_NLS_ISO8859_15=m ++CONFIG_NLS_KOI8_R=m ++CONFIG_NLS_KOI8_U=m ++CONFIG_NLS_UTF8=m ++CONFIG_PRINTK_TIME=y ++CONFIG_DETECT_HUNG_TASK=y ++CONFIG_TIMER_STATS=y ++CONFIG_DEBUG_STACK_USAGE=y ++CONFIG_DEBUG_INFO=y ++CONFIG_DEBUG_MEMORY_INIT=y ++CONFIG_BOOT_PRINTK_DELAY=y ++CONFIG_LATENCYTOP=y ++CONFIG_SYSCTL_SYSCALL_CHECK=y ++CONFIG_IRQSOFF_TRACER=y ++CONFIG_SCHED_TRACER=y ++CONFIG_STACK_TRACER=y ++CONFIG_BLK_DEV_IO_TRACE=y ++CONFIG_FUNCTION_PROFILER=y ++CONFIG_KGDB=y ++CONFIG_KGDB_KDB=y ++CONFIG_KDB_KEYBOARD=y ++CONFIG_STRICT_DEVMEM=y ++CONFIG_CRYPTO_AUTHENC=m ++CONFIG_CRYPTO_SEQIV=m ++CONFIG_CRYPTO_CBC=y ++CONFIG_CRYPTO_HMAC=y ++CONFIG_CRYPTO_XCBC=m ++CONFIG_CRYPTO_MD5=y ++CONFIG_CRYPTO_SHA1=y ++CONFIG_CRYPTO_SHA256=m ++CONFIG_CRYPTO_SHA512=m ++CONFIG_CRYPTO_TGR192=m ++CONFIG_CRYPTO_WP512=m ++CONFIG_CRYPTO_CAST5=m ++CONFIG_CRYPTO_DES=y ++CONFIG_CRYPTO_DEFLATE=m ++# CONFIG_CRYPTO_ANSI_CPRNG is not set ++# CONFIG_CRYPTO_HW is not set ++CONFIG_CRC_ITU_T=y ++CONFIG_LIBCRC32C=y +--- a/arch/arm/mach-bcm2708/Kconfig ++++ b/arch/arm/mach-bcm2708/Kconfig +@@ -22,4 +22,11 @@ config BCM2708_VCMEM + help + Helper for videocore memory access and total size allocation. + ++config BCM2708_NOL2CACHE ++ bool "Videocore L2 cache disable" ++ depends on MACH_BCM2708 ++ default n ++ help ++ Do not allow ARM to use GPU's L2 cache. Requires disable_l2cache in config.txt. ++ + endmenu +--- a/arch/arm/mach-bcm2708/bcm2708.c ++++ b/arch/arm/mach-bcm2708/bcm2708.c +@@ -29,6 +29,7 @@ + #include <linux/clockchips.h> + #include <linux/cnt32_to_63.h> + #include <linux/io.h> ++#include <linux/module.h> + + #include <linux/version.h> + #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,38) +@@ -68,6 +69,9 @@ + */ + #define DMA_MASK_BITS_COMMON 32 + ++/* command line parameters */ ++static unsigned boardrev, serial; ++ + static void __init bcm2708_init_led(void); + + void __init bcm2708_init_irq(void) +@@ -77,58 +81,57 @@ void __init bcm2708_init_irq(void) + + static struct map_desc bcm2708_io_desc[] __initdata = { + { +- .virtual = IO_ADDRESS(ARMCTRL_BASE), +- .pfn = __phys_to_pfn(ARMCTRL_BASE), +- .length = SZ_4K, +- .type = MT_DEVICE +- }, { +- .virtual = IO_ADDRESS(UART0_BASE), +- .pfn = __phys_to_pfn(UART0_BASE), +- .length = SZ_4K, +- .type = MT_DEVICE +- }, { +- .virtual = IO_ADDRESS(UART1_BASE), +- .pfn = __phys_to_pfn(UART1_BASE), +- .length = SZ_4K, +- .type = MT_DEVICE +- }, { +-#ifdef CONFIG_MMC_BCM2708 /* broadcom legacy SD */ +- .virtual = IO_ADDRESS(MMCI0_BASE), +- .pfn = __phys_to_pfn(MMCI0_BASE), +- .length = SZ_4K, +- .type = MT_DEVICE +- }, { +-#endif +- .virtual = IO_ADDRESS(DMA_BASE), +- .pfn = __phys_to_pfn(DMA_BASE), +- .length = SZ_4K, +- .type = MT_DEVICE +- }, { +- .virtual = IO_ADDRESS(MCORE_BASE), +- .pfn = __phys_to_pfn(MCORE_BASE), +- .length = SZ_4K, +- .type = MT_DEVICE +- }, { +- .virtual = IO_ADDRESS(ST_BASE), +- .pfn = __phys_to_pfn(ST_BASE), +- .length = SZ_4K, +- .type = MT_DEVICE +- }, { +- .virtual = IO_ADDRESS(USB_BASE), +- .pfn = __phys_to_pfn(USB_BASE), +- .length = SZ_128K, +- .type = MT_DEVICE +- }, { +- .virtual = IO_ADDRESS(PM_BASE), +- .pfn = __phys_to_pfn(PM_BASE), +- .length = SZ_4K, +- .type = MT_DEVICE +- }, { +- .virtual = IO_ADDRESS(GPIO_BASE), +- .pfn = __phys_to_pfn(GPIO_BASE), +- .length = SZ_4K, +- .type = MT_DEVICE +- } ++ .virtual = IO_ADDRESS(ARMCTRL_BASE), ++ .pfn = __phys_to_pfn(ARMCTRL_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(UART0_BASE), ++ .pfn = __phys_to_pfn(UART0_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(UART1_BASE), ++ .pfn = __phys_to_pfn(UART1_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++#ifdef CONFIG_MMC_BCM2708 /* broadcom legacy SD */ ++ { ++ .virtual = IO_ADDRESS(MMCI0_BASE), ++ .pfn = __phys_to_pfn(MMCI0_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++#endif ++ { ++ .virtual = IO_ADDRESS(DMA_BASE), ++ .pfn = __phys_to_pfn(DMA_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(MCORE_BASE), ++ .pfn = __phys_to_pfn(MCORE_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(ST_BASE), ++ .pfn = __phys_to_pfn(ST_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(USB_BASE), ++ .pfn = __phys_to_pfn(USB_BASE), ++ .length = SZ_128K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(PM_BASE), ++ .pfn = __phys_to_pfn(PM_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE}, ++ { ++ .virtual = IO_ADDRESS(GPIO_BASE), ++ .pfn = __phys_to_pfn(GPIO_BASE), ++ .length = SZ_4K, ++ .type = MT_DEVICE} + }; + + void __init bcm2708_map_io(void) +@@ -136,74 +139,91 @@ void __init bcm2708_map_io(void) + iotable_init(bcm2708_io_desc, ARRAY_SIZE(bcm2708_io_desc)); + } + +-unsigned long frc_clock_ticks32(void) ++// The STC is a free running counter that increments at the rate of 1MHz ++#define STC_FREQ_HZ 1000000 ++ ++static cycle_t stc_read_cycles(struct clocksource *cs) + { + /* STC: a free running counter that increments at the rate of 1MHz */ +- return readl(__io_address(ST_BASE+0x04)); ++ return (cycle_t) readl(__io_address(ST_BASE + 0x04)); + } + +-unsigned long long frc_clock_ticks63(void) ++static struct clocksource clocksource_stc = { ++ .name = "stc", ++ .rating = 300, ++ .read = stc_read_cycles, ++ .mask = CLOCKSOURCE_MASK(32), ++ .flags = CLOCK_SOURCE_IS_CONTINUOUS, ++}; ++ ++unsigned long frc_clock_ticks32(void) + { +- unsigned long t = frc_clock_ticks32(); +- /* For cnt32_to_63 to work correctly we MUST call this routine +- * at least once every half-32-bit-wraparound period - that's once +- * every 35minutes or so - using it in sched_clock() should ensure this +- */ +- return cnt32_to_63(t); ++ return (unsigned long)stc_read_cycles(&clocksource_stc); ++} ++ ++static void __init bcm2708_clocksource_init(void) ++{ ++ // calculate .shift and .mult values and register clocksource ++ if (clocksource_register_hz(&clocksource_stc, STC_FREQ_HZ)) { ++ printk(KERN_ERR "timer: failed to initialize clock " ++ "source %s\n", clocksource_stc.name); ++ } + } + + unsigned long long sched_clock(void) + { +- return 1000ull * frc_clock_ticks63(); ++ return clocksource_cyc2ns(clocksource_stc.read(&clocksource_stc), ++ clocksource_stc.mult, clocksource_stc.shift); + } + + /* + * These are fixed clocks. + */ + static struct clk ref24_clk = { +- .rate = 3000000, /* The UART is clocked at 3MHz via APB_CLK */ ++ .rate = 3000000, /* The UART is clocked at 3MHz via APB_CLK */ + }; ++ + static struct clk osc_clk = { + #ifdef CONFIG_ARCH_BCM2708_CHIPIT +- .rate = 27000000, ++ .rate = 27000000, + #else +- .rate = 500000000, /* ARM clock is set from the VideoCore booter */ ++ .rate = 500000000, /* ARM clock is set from the VideoCore booter */ + #endif + }; ++ + /* warning - the USB needs a clock > 34MHz */ + + #ifdef CONFIG_MMC_BCM2708 + static struct clk sdhost_clk = { + #ifdef CONFIG_ARCH_BCM2708_CHIPIT +- .rate = 4000000, /* 4MHz */ ++ .rate = 4000000, /* 4MHz */ + #else +- .rate = 250000000, /* 250MHz */ ++ .rate = 250000000, /* 250MHz */ + #endif + }; + #endif + + static struct clk_lookup lookups[] = { +- { /* UART0 */ +- .dev_id = "dev:f1", +- .clk = &ref24_clk, +- }, +- { /* USB */ +- .dev_id = "bcm2708_usb", +- .clk = &osc_clk, ++ { /* UART0 */ ++ .dev_id = "dev:f1", ++ .clk = &ref24_clk, ++ }, ++ { /* USB */ ++ .dev_id = "bcm2708_usb", ++ .clk = &osc_clk, + #ifdef CONFIG_MMC_BCM2708 +- }, +- { /* MCI */ +- .dev_id = "bcm2708_mci.0", +- .clk = &sdhost_clk, ++ }, ++ { /* MCI */ ++ .dev_id = "bcm2708_mci.0", ++ .clk = &sdhost_clk, + #endif +- } ++ } + }; + +- + #define UART0_IRQ { IRQ_UART, NO_IRQ } + #define UART0_DMA { 15, 14 } + +-AMBA_DEVICE(uart0, "dev:f1", UART0, NULL); ++AMBA_DEVICE(uart0, "dev:f1", UART0, NULL); + + static struct amba_device *amba_devs[] __initdata = { + &uart0_device, +@@ -211,262 +231,232 @@ static struct amba_device *amba_devs[] _ + + static struct resource bcm2708_dmaman_resources[] = { + { +- .start = DMA_BASE, +- .end = DMA_BASE + SZ_4K - 1, +- .flags = IORESOURCE_MEM, +- } ++ .start = DMA_BASE, ++ .end = DMA_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ } + }; + + static struct platform_device bcm2708_dmaman_device = { +- .name = BCM_DMAMAN_DRIVER_NAME, +- .id = 0, /* first bcm2708_dma */ +- .resource = bcm2708_dmaman_resources, +- .num_resources = ARRAY_SIZE(bcm2708_dmaman_resources), ++ .name = BCM_DMAMAN_DRIVER_NAME, ++ .id = 0, /* first bcm2708_dma */ ++ .resource = bcm2708_dmaman_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_dmaman_resources), + }; + + #ifdef CONFIG_MMC_BCM2708 + static struct resource bcm2708_mci_resources[] = { + { +- .start = MMCI0_BASE, +- .end = MMCI0_BASE + SZ_4K - 1, +- .flags = IORESOURCE_MEM, +- }, { +- .start = IRQ_SDIO, +- .end = IRQ_SDIO, +- .flags = IORESOURCE_IRQ, +- } ++ .start = MMCI0_BASE, ++ .end = MMCI0_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = IRQ_SDIO, ++ .end = IRQ_SDIO, ++ .flags = IORESOURCE_IRQ, ++ } + }; + +- + static struct platform_device bcm2708_mci_device = { +- .name = "bcm2708_mci", +- .id = 0, /* first bcm2708_mci */ +- .resource = bcm2708_mci_resources, +- .num_resources = ARRAY_SIZE(bcm2708_mci_resources), +- .dev = { +- .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), +- }, ++ .name = "bcm2708_mci", ++ .id = 0, /* first bcm2708_mci */ ++ .resource = bcm2708_mci_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_mci_resources), ++ .dev = { ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, + }; + #endif /* CONFIG_MMC_BCM2708 */ + +- + static u64 fb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + static struct platform_device bcm2708_fb_device = { +- .name = "bcm2708_fb", +- .id = -1, /* only one bcm2708_fb */ +- .resource = NULL, +- .num_resources = 0, +- .dev = { +- .dma_mask = &fb_dmamask, +- .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), +- }, ++ .name = "bcm2708_fb", ++ .id = -1, /* only one bcm2708_fb */ ++ .resource = NULL, ++ .num_resources = 0, ++ .dev = { ++ .dma_mask = &fb_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, + }; + + static struct plat_serial8250_port bcm2708_uart1_platform_data[] = { + { +- .mapbase = UART1_BASE + 0x40, +- .irq = IRQ_AUX, +- .uartclk = 125000000, +- .regshift = 2, +- .iotype = UPIO_MEM, +- .flags = UPF_FIXED_TYPE | UPF_IOREMAP | UPF_SKIP_TEST, +- .type = PORT_8250, +- }, +- { }, ++ .mapbase = UART1_BASE + 0x40, ++ .irq = IRQ_AUX, ++ .uartclk = 125000000, ++ .regshift = 2, ++ .iotype = UPIO_MEM, ++ .flags = UPF_FIXED_TYPE | UPF_IOREMAP | UPF_SKIP_TEST, ++ .type = PORT_8250, ++ }, ++ {}, + }; + + static struct platform_device bcm2708_uart1_device = { +- .name = "serial8250", +- .id = PLAT8250_DEV_PLATFORM, +- .dev = { +- .platform_data = bcm2708_uart1_platform_data, +- }, ++ .name = "serial8250", ++ .id = PLAT8250_DEV_PLATFORM, ++ .dev = { ++ .platform_data = bcm2708_uart1_platform_data, ++ }, + }; + + static struct resource bcm2708_usb_resources[] = { +- [0] = { +- .start = USB_BASE, +- .end = USB_BASE + SZ_128K - 1, +- .flags = IORESOURCE_MEM, +- }, +- [1] = { +- .start = IRQ_USB, +- .end = IRQ_USB, +- .flags = IORESOURCE_IRQ, +- }, ++ [0] = { ++ .start = USB_BASE, ++ .end = USB_BASE + SZ_128K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ [1] = { ++ .start = IRQ_USB, ++ .end = IRQ_USB, ++ .flags = IORESOURCE_IRQ, ++ }, + }; + + static u64 usb_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + static struct platform_device bcm2708_usb_device = { +- .name = "bcm2708_usb", +- .id = -1, /* only one bcm2708_usb */ +- .resource = bcm2708_usb_resources, +- .num_resources = ARRAY_SIZE(bcm2708_usb_resources), +- .dev = { +- .dma_mask = &usb_dmamask, +- .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), +- }, ++ .name = "bcm2708_usb", ++ .id = -1, /* only one bcm2708_usb */ ++ .resource = bcm2708_usb_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_usb_resources), ++ .dev = { ++ .dma_mask = &usb_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, + }; + + static struct resource bcm2708_vcio_resources[] = { +- [0] = { /* mailbox/semaphore/doorbell access */ +- .start = MCORE_BASE, +- .end = MCORE_BASE + SZ_4K - 1, +- .flags = IORESOURCE_MEM, +- }, ++ [0] = { /* mailbox/semaphore/doorbell access */ ++ .start = MCORE_BASE, ++ .end = MCORE_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, + }; + + static u64 vcio_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + static struct platform_device bcm2708_vcio_device = { +- .name = BCM_VCIO_DRIVER_NAME, +- .id = -1, /* only one VideoCore I/O area */ +- .resource = bcm2708_vcio_resources, +- .num_resources = ARRAY_SIZE(bcm2708_vcio_resources), +- .dev = { +- .dma_mask = &vcio_dmamask, +- .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), +- }, ++ .name = BCM_VCIO_DRIVER_NAME, ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_vcio_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_vcio_resources), ++ .dev = { ++ .dma_mask = &vcio_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, + }; + + #ifdef CONFIG_BCM2708_GPIO + #define BCM_GPIO_DRIVER_NAME "bcm2708_gpio" + + static struct resource bcm2708_gpio_resources[] = { +- [0] = { /* general purpose I/O */ +- .start = GPIO_BASE, +- .end = GPIO_BASE + SZ_4K - 1, +- .flags = IORESOURCE_MEM, +- }, ++ [0] = { /* general purpose I/O */ ++ .start = GPIO_BASE, ++ .end = GPIO_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, + }; + + static u64 gpio_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + static struct platform_device bcm2708_gpio_device = { +- .name = BCM_GPIO_DRIVER_NAME, +- .id = -1, /* only one VideoCore I/O area */ +- .resource = bcm2708_gpio_resources, +- .num_resources = ARRAY_SIZE(bcm2708_gpio_resources), +- .dev = { +- .dma_mask = &gpio_dmamask, +- .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), +- }, +-}; +-#endif +- +-#ifdef CONFIG_BCM2708_BUTTONS +-static struct resource bcm2708_vcbuttons_resources[] = { +-}; +- +-static u64 vcbuttons_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); +- +-static struct platform_device bcm2708_vcbuttons_device = { +- .name = "bcm2708_vcbuttons", +- .id = -1, /* only one VideoCore I/O area */ +- .resource = bcm2708_vcbuttons_resources, +- .num_resources = ARRAY_SIZE(bcm2708_vcbuttons_resources), +- .dev = { +- .dma_mask = &vcbuttons_dmamask, +- .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), +- }, +-}; +-#endif +- +-#ifdef CONFIG_BCM2708_TOUCHSCREEN +-static struct resource bcm2708_vctouch_resources[] = { +-}; +- +-static u64 vctouch_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); +- +-static struct platform_device bcm2708_vctouch_device = { +- .name = "bcm2708_vctouch", +- .id = -1, /* only one VideoCore I/O area */ +- .resource = bcm2708_vctouch_resources, +- .num_resources = ARRAY_SIZE(bcm2708_vctouch_resources), +- .dev = { +- .dma_mask = &vctouch_dmamask, +- .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), +- }, ++ .name = BCM_GPIO_DRIVER_NAME, ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_gpio_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_gpio_resources), ++ .dev = { ++ .dma_mask = &gpio_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, + }; + #endif + + static struct resource bcm2708_systemtimer_resources[] = { +- [0] = { /* system timer access */ +- .start = ST_BASE, +- .end = ST_BASE + SZ_4K - 1, +- .flags = IORESOURCE_MEM, +- }, { +- .start = IRQ_TIMER3, +- .end = IRQ_TIMER3, +- .flags = IORESOURCE_IRQ, +- } +- ++ [0] = { /* system timer access */ ++ .start = ST_BASE, ++ .end = ST_BASE + SZ_4K - 1, ++ .flags = IORESOURCE_MEM, ++ }, ++ { ++ .start = IRQ_TIMER3, ++ .end = IRQ_TIMER3, ++ .flags = IORESOURCE_IRQ, ++ } + + }; + + static u64 systemtimer_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + static struct platform_device bcm2708_systemtimer_device = { +- .name = "bcm2708_systemtimer", +- .id = -1, /* only one VideoCore I/O area */ +- .resource = bcm2708_systemtimer_resources, +- .num_resources = ARRAY_SIZE(bcm2708_systemtimer_resources), +- .dev = { +- .dma_mask = &systemtimer_dmamask, +- .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), +- }, ++ .name = "bcm2708_systemtimer", ++ .id = -1, /* only one VideoCore I/O area */ ++ .resource = bcm2708_systemtimer_resources, ++ .num_resources = ARRAY_SIZE(bcm2708_systemtimer_resources), ++ .dev = { ++ .dma_mask = &systemtimer_dmamask, ++ .coherent_dma_mask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON), ++ }, + }; + +-#ifdef CONFIG_MMC_SDHCI_BCM2708 /* Arasan emmc SD */ ++#ifdef CONFIG_MMC_SDHCI_BCM2708 /* Arasan emmc SD */ + static struct resource bcm2708_emmc_resources[] = { + [0] = { +- .start = EMMC_BASE, +- .end = EMMC_BASE + SZ_256 - 1, /* we only need this area */ +- /* the memory map actually makes SZ_4K available */ +- .flags = IORESOURCE_MEM, +- }, ++ .start = EMMC_BASE, ++ .end = EMMC_BASE + SZ_256 - 1, /* we only need this area */ ++ /* the memory map actually makes SZ_4K available */ ++ .flags = IORESOURCE_MEM, ++ }, + [1] = { +- .start = IRQ_ARASANSDIO, +- .end = IRQ_ARASANSDIO, +- .flags = IORESOURCE_IRQ, +- }, ++ .start = IRQ_ARASANSDIO, ++ .end = IRQ_ARASANSDIO, ++ .flags = IORESOURCE_IRQ, ++ }, + }; + + static u64 bcm2708_emmc_dmamask = 0xffffffffUL; + + struct platform_device bcm2708_emmc_device = { +- .name = "bcm2708_sdhci", +- .id = 0, +- .num_resources = ARRAY_SIZE(bcm2708_emmc_resources), +- .resource = bcm2708_emmc_resources, +- .dev = { +- .dma_mask = &bcm2708_emmc_dmamask, +- .coherent_dma_mask = 0xffffffffUL +- }, ++ .name = "bcm2708_sdhci", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_emmc_resources), ++ .resource = bcm2708_emmc_resources, ++ .dev = { ++ .dma_mask = &bcm2708_emmc_dmamask, ++ .coherent_dma_mask = 0xffffffffUL}, + }; + #endif /* CONFIG_MMC_SDHCI_BCM2708 */ + + static struct resource bcm2708_powerman_resources[] = { + [0] = { +- .start = PM_BASE, +- .end = PM_BASE + SZ_256 - 1, +- .flags = IORESOURCE_MEM, +- }, ++ .start = PM_BASE, ++ .end = PM_BASE + SZ_256 - 1, ++ .flags = IORESOURCE_MEM, ++ }, + }; + + static u64 powerman_dmamask = DMA_BIT_MASK(DMA_MASK_BITS_COMMON); + + struct platform_device bcm2708_powerman_device = { +- .name = "bcm2708_powerman", +- .id = 0, +- .num_resources = ARRAY_SIZE(bcm2708_powerman_resources), +- .resource = bcm2708_powerman_resources, +- .dev = { +- .dma_mask = &powerman_dmamask, +- .coherent_dma_mask = 0xffffffffUL +- }, ++ .name = "bcm2708_powerman", ++ .id = 0, ++ .num_resources = ARRAY_SIZE(bcm2708_powerman_resources), ++ .resource = bcm2708_powerman_resources, ++ .dev = { ++ .dma_mask = &powerman_dmamask, ++ .coherent_dma_mask = 0xffffffffUL}, ++}; ++ ++static struct platform_device bcm2708_alsa_devices[] = { ++ [0] = { ++ .name = "bcm2835_AUD0", ++ .id = 0, /* first audio device */ ++ .resource = 0, ++ .num_resources = 0, ++ }, + }; + + int __init bcm_register_device(struct platform_device *pdev) +@@ -500,30 +490,29 @@ void __init bcm2708_init(void) + bcm_register_device(&bcm2708_fb_device); + bcm_register_device(&bcm2708_usb_device); + bcm_register_device(&bcm2708_uart1_device); +-#ifdef CONFIG_BCM2708_BUTTONS +- bcm_register_device(&bcm2708_vcbuttons_device); +-#endif +-#ifdef CONFIG_BCM2708_TOUCHSCREEN +- bcm_register_device(&bcm2708_vctouch_device); +-#endif + bcm_register_device(&bcm2708_powerman_device); + #ifdef CONFIG_MMC_SDHCI_BCM2708 + bcm_register_device(&bcm2708_emmc_device); + #endif +- bcm2708_init_led(); ++ bcm2708_init_led(); ++ for (i = 0; i < ARRAY_SIZE(bcm2708_alsa_devices); i++) ++ bcm_register_device(&bcm2708_alsa_devices[i]); ++ + #ifdef CONFIG_BCM2708_VCMEM +-{ +- extern void vc_mem_connected_init(void); +- vc_mem_connected_init(); +-} ++ { ++ extern void vc_mem_connected_init(void); ++ vc_mem_connected_init(); ++ } + #endif + for (i = 0; i < ARRAY_SIZE(amba_devs); i++) { + struct amba_device *d = amba_devs[i]; + amba_device_register(d, &iomem_resource); + } ++ system_rev = boardrev; ++ system_serial_low = serial; + } + +-#define TIMER_PERIOD 10000 /* HZ in microsecs */ ++#define TIMER_PERIOD 10000 /* HZ in microsecs */ + + static void timer_set_mode(enum clock_event_mode mode, + struct clock_event_device *clk) +@@ -532,37 +521,36 @@ static void timer_set_mode(enum clock_ev + + switch (mode) { + case CLOCK_EVT_MODE_PERIODIC: +- stc = readl(__io_address(ST_BASE+0x04)); +- writel(stc + TIMER_PERIOD, +- __io_address(ST_BASE+0x18));/* stc3 */ ++ stc = readl(__io_address(ST_BASE + 0x04)); ++ writel(stc + TIMER_PERIOD, __io_address(ST_BASE + 0x18)); /* stc3 */ + break; + case CLOCK_EVT_MODE_ONESHOT: + case CLOCK_EVT_MODE_UNUSED: + case CLOCK_EVT_MODE_SHUTDOWN: + default: + printk(KERN_ERR "timer_set_mode: unhandled mode:%d\n", +- (int)mode); ++ (int)mode); + break; + } + + } + +-static int timer_set_next_event(unsigned long evt, ++static int timer_set_next_event(unsigned long cycles, + struct clock_event_device *unused) + { + unsigned long stc; + +- stc = readl(__io_address(ST_BASE + 0x04)); +- writel(stc + TIMER_PERIOD, __io_address(ST_BASE+0x18)); /* stc3 */ ++ stc = readl(__io_address(ST_BASE + 0x04)); ++ writel(stc + cycles, __io_address(ST_BASE + 0x18)); /* stc3 */ + return 0; + } + +-static struct clock_event_device timer0_clockevent = { +- .name = "timer0", +- .shift = 32, +- .features = CLOCK_EVT_FEAT_ONESHOT, +- .set_mode = timer_set_mode, +- .set_next_event = timer_set_next_event, ++static struct clock_event_device timer0_clockevent = { ++ .name = "timer0", ++ .shift = 32, ++ .features = CLOCK_EVT_FEAT_ONESHOT, ++ .set_mode = timer_set_mode, ++ .set_next_event = timer_set_next_event, + }; + + /* +@@ -572,7 +560,7 @@ static irqreturn_t bcm2708_timer_interru + { + struct clock_event_device *evt = &timer0_clockevent; + +- writel(1<<3, __io_address(ST_BASE+0x00)); /* stcs clear timer int */ ++ writel(1 << 3, __io_address(ST_BASE + 0x00)); /* stcs clear timer int */ + + evt->event_handler(evt); + +@@ -580,9 +568,9 @@ static irqreturn_t bcm2708_timer_interru + } + + static struct irqaction bcm2708_timer_irq = { +- .name = "BCM2708 Timer Tick", +- .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, +- .handler = bcm2708_timer_interrupt, ++ .name = "BCM2708 Timer Tick", ++ .flags = IRQF_DISABLED | IRQF_TIMER | IRQF_IRQPOLL, ++ .handler = bcm2708_timer_interrupt, + }; + + /* +@@ -590,6 +578,9 @@ static struct irqaction bcm2708_timer_ir + */ + static void __init bcm2708_timer_init(void) + { ++ /* init high res timer */ ++ bcm2708_clocksource_init(); ++ + /* + * Initialise to a known state (all timers off) + */ +@@ -600,18 +591,18 @@ static void __init bcm2708_timer_init(vo + setup_irq(IRQ_TIMER3, &bcm2708_timer_irq); + + timer0_clockevent.mult = +- div_sc(1000000, NSEC_PER_SEC, timer0_clockevent.shift); ++ div_sc(STC_FREQ_HZ, NSEC_PER_SEC, timer0_clockevent.shift); + timer0_clockevent.max_delta_ns = +- clockevent_delta2ns(0xffffffff, &timer0_clockevent); ++ clockevent_delta2ns(0xffffffff, &timer0_clockevent); + timer0_clockevent.min_delta_ns = +- clockevent_delta2ns(0xf, &timer0_clockevent); ++ clockevent_delta2ns(0xf, &timer0_clockevent); + + timer0_clockevent.cpumask = cpumask_of(0); + clockevents_register_device(&timer0_clockevent); + } + + struct sys_timer bcm2708_timer = { +- .init = bcm2708_timer_init, ++ .init = bcm2708_timer_init, + }; + + #if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE) +@@ -619,24 +610,24 @@ struct sys_timer bcm2708_timer = { + + static struct gpio_led bcm2708_leds[] = { + [0] = { +- .gpio = 16, +- .name = "led0", +- .default_trigger = "mmc0", +- .active_low = 0, +- }, ++ .gpio = 16, ++ .name = "led0", ++ .default_trigger = "mmc0", ++ .active_low = 1, ++ }, + }; + + static struct gpio_led_platform_data bcm2708_led_pdata = { +- .num_leds = ARRAY_SIZE(bcm2708_leds), +- .leds = bcm2708_leds, ++ .num_leds = ARRAY_SIZE(bcm2708_leds), ++ .leds = bcm2708_leds, + }; + + static struct platform_device bcm2708_led_device = { +- .name = "leds-gpio", +- .id = -1, +- .dev = { +- .platform_data = &bcm2708_led_pdata, +- }, ++ .name = "leds-gpio", ++ .id = -1, ++ .dev = { ++ .platform_data = &bcm2708_led_pdata, ++ }, + }; + + static void __init bcm2708_init_led(void) +@@ -644,14 +635,14 @@ static void __init bcm2708_init_led(void + platform_device_register(&bcm2708_led_device); + } + #else +-static inline void bcm2708_init_led(void) {} ++static inline void bcm2708_init_led(void) ++{ ++} + #endif + +- + MACHINE_START(BCM2708, "BCM2708") +- /* Maintainer: Broadcom Europe Ltd. */ +- .map_io = bcm2708_map_io, +- .init_irq = bcm2708_init_irq, +- .timer = &bcm2708_timer, +- .init_machine = bcm2708_init, +-MACHINE_END ++ /* Maintainer: Broadcom Europe Ltd. */ ++ .map_io = bcm2708_map_io,.init_irq = bcm2708_init_irq,.timer = ++ &bcm2708_timer,.init_machine = ++ bcm2708_init, MACHINE_END module_param(boardrev, uint, 0644); ++module_param(serial, uint, 0644); +--- a/arch/arm/mach-bcm2708/include/mach/memory.h ++++ b/arch/arm/mach-bcm2708/include/mach/memory.h +@@ -32,9 +32,14 @@ + /* + * Physical DRAM offset. + */ +-#define PHYS_OFFSET UL(0x00000000) ++#define PLAT_PHYS_OFFSET UL(0x00000000) + #define ARMMEM_OFFSET UL(0x00000000) /* offset in VC of ARM memory */ +-#define _REAL_BUS_OFFSET UL(0xC0000000) /* don't use L1 or L2 caches */ ++ ++#ifdef CONFIG_BCM2708_NOL2CACHE ++ #define _REAL_BUS_OFFSET UL(0xC0000000) /* don't use L1 or L2 caches */ ++#else ++ #define _REAL_BUS_OFFSET UL(0x40000000) /* use L2 cache */ ++#endif + + /* We're using the memory at 64M in the VideoCore for Linux - this adjustment + * will provide the offset into this area as well as setting the bits that +@@ -46,8 +51,8 @@ + #define BUS_OFFSET (ARMMEM_OFFSET + _REAL_BUS_OFFSET) + #define __virt_to_bus(x) ((x) + (BUS_OFFSET - PAGE_OFFSET)) + #define __bus_to_virt(x) ((x) - (BUS_OFFSET - PAGE_OFFSET)) +-#define __pfn_to_bus(x) (__pfn_to_phys(x) + (BUS_OFFSET - PHYS_OFFSET)) +-#define __bus_to_pfn(x) __phys_to_pfn((x) - (BUS_OFFSET - PHYS_OFFSET)) ++#define __pfn_to_bus(x) (__pfn_to_phys(x) + (BUS_OFFSET - PLAT_PHYS_OFFSET)) ++#define __bus_to_pfn(x) __phys_to_pfn((x) - (BUS_OFFSET - PLAT_PHYS_OFFSET)) + + /* + * Consistent DMA area set to 2M. Framebuffer now allocated on host +--- a/arch/arm/mach-bcm2708/include/mach/vc_mem.h ++++ b/arch/arm/mach-bcm2708/include/mach/vc_mem.h +@@ -21,6 +21,7 @@ + + #define VC_MEM_IOC_MEM_PHYS_ADDR _IOR( VC_MEM_IOC_MAGIC, 0, unsigned long ) + #define VC_MEM_IOC_MEM_SIZE _IOR( VC_MEM_IOC_MAGIC, 1, unsigned int ) ++#define VC_MEM_IOC_MEM_BASE _IOR( VC_MEM_IOC_MAGIC, 2, unsigned int ) + + #if defined( __KERNEL__ ) + #define VC_MEM_TO_ARM_ADDR_MASK 0x3FFFFFFF +--- a/arch/arm/mach-bcm2708/power.c ++++ b/arch/arm/mach-bcm2708/power.c +@@ -14,6 +14,7 @@ + #include <linux/module.h> + #include <linux/semaphore.h> + #include <linux/bug.h> ++#include <linux/delay.h> + #include <mach/power.h> + #include <mach/vcio.h> + #include <mach/arm_power.h> +@@ -96,7 +97,6 @@ int bcm_power_request(BCM_POWER_HANDLE_T + bcm_mailbox_write(MBOX_CHAN_POWER, + global_request << 4); + +- /* Wait for a response during power-up */ + if (global_request & ~g_state.global_request) { + rc = bcm_mailbox_read(MBOX_CHAN_POWER, + &actual); +@@ -111,14 +111,14 @@ int bcm_power_request(BCM_POWER_HANDLE_T + + if (rc == 0) { + if (actual != global_request) { +- printk(KERN_ERR +- "%s: prev global %x, new global %x, actual %x, request %x, others_request %x\n", ++ printk(KERN_INFO ++ "%s: Fail: prev global %x, new global %x, actual %x request %x, others_request %x\n", + __func__, + g_state.global_request, + global_request, actual, request, others_request); + /* A failure */ +- BUG_ON((others_request & actual) +- != others_request); ++ // BUG_ON((others_request & actual) ++ // != others_request); + request &= actual; + rc = -EIO; + } +@@ -161,6 +161,7 @@ static int __init bcm_power_init(void) + int i; + + printk(KERN_INFO "bcm_power: Broadcom power driver\n"); ++ bcm_mailbox_write(MBOX_CHAN_POWER, 0); + + for (i = 0; i < BCM_POWER_MAXCLIENTS; i++) + g_state.client_request[i] = BCM_POWER_NOCLIENT; +--- a/arch/arm/mach-bcm2708/vc_mem.c ++++ b/arch/arm/mach-bcm2708/vc_mem.c +@@ -85,9 +85,11 @@ unsigned long mm_vc_mem_phys_addr = MM_A + #endif + + unsigned int mm_vc_mem_size = 0; ++unsigned int mm_vc_mem_base = 0; + + EXPORT_SYMBOL(mm_vc_mem_phys_addr); + EXPORT_SYMBOL(mm_vc_mem_size); ++EXPORT_SYMBOL(mm_vc_mem_base); + + /**************************************************************************** + * +@@ -132,36 +134,19 @@ vc_mem_release(struct inode *inode, stru + static void + vc_mem_get_size(void) + { +-#ifdef CONFIG_ARCH_BCM2708 + mm_vc_mem_size = 256 * 1024 * 1024; // Static for now +-#else +- CHAL_IPC_HANDLE ipc_handle; +- uint32_t wakeup_register; +- +- // Get the videocore memory size from the IPC mailbox if not yet +- // assigned. +- if (mm_vc_mem_size == 0) { +- ipc_handle = chal_ipc_config(NULL); +- if (ipc_handle == NULL) { +- LOG_ERR("%s: failed to get IPC handlle", __func__); +- return; +- } ++} + +- chal_ipc_query_wakeup_vc(ipc_handle, &wakeup_register); +- if ((wakeup_register & ~1) == 0) { +- LOG_DBG("%s: videocore not yet loaded, skipping...", +- __func__); +- } else { +- if (chal_ipc_read_mailbox(ipc_handle, +- IPC_MAILBOX_ID_0, +- &mm_vc_mem_size) != +- BCM_SUCCESS) { +- LOG_ERR("%s: failed to read from IPC mailbox", +- __func__); +- } +- } +- } +-#endif ++/**************************************************************************** ++* ++* vc_mem_get_base ++* ++***************************************************************************/ ++ ++static void ++vc_mem_get_base(void) ++{ ++ mm_vc_mem_base = 128 * 1024 * 1024; // Static for now + } + + /**************************************************************************** +@@ -220,6 +205,20 @@ vc_mem_ioctl(struct file *file, unsigned + rc = -EFAULT; + } + break; ++ } ++ case VC_MEM_IOC_MEM_BASE: ++ { ++ // Get the videocore memory base ++ vc_mem_get_base(); ++ ++ LOG_DBG("%s: VC_MEM_IOC_MEM_BASE=%u", __func__, ++ mm_vc_mem_base); ++ ++ if (copy_to_user((void *) arg, &mm_vc_mem_base, ++ sizeof (mm_vc_mem_base)) != 0) { ++ rc = -EFAULT; ++ } ++ break; + } + default: + { +--- a/arch/arm/mach-bcm2708/vcio.c ++++ b/arch/arm/mach-bcm2708/vcio.c +@@ -119,8 +119,7 @@ static int mbox_read(struct vc_mailbox * + if (mbox->magic != MBOX_MAGIC) + rc = -EINVAL; + else { +- if (mbox->msg[chan] || +- (down_interruptible(&mbox->sema[chan]) == 0)) { ++ if (down_interruptible(&mbox->sema[chan]) == 0) { + *data28 = MBOX_DATA28(mbox->msg[chan]); + mbox->msg[chan] = 0; + rc = 0; +--- a/drivers/misc/vc04_services/Makefile ++++ b/drivers/misc/vc04_services/Makefile +@@ -2,6 +2,8 @@ obj-$(CONFIG_BCM2708_VCHIQ) += vchiq.o + + vchiq-objs := \ + interface/vchiq_arm/vchiq_core.o \ ++ interface/vchiq_arm/vchiq_shim.o \ ++ interface/vchiq_arm/vchiq_util.o \ + interface/vchiq_arm/vchiq_arm.o \ + interface/vchiq_arm/vchiq_kern_lib.o \ + interface/vchiq_arm/vchiq_2835_arm.o \ +@@ -13,7 +15,7 @@ vchiq-objs := \ + interface/vcos/generic/vcos_mem_from_malloc.o \ + interface/vcos/generic/vcos_cmd.o + +-EXTRA_CFLAGS += -DVCOS_VERIFY_BKPTS=1 -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel ++EXTRA_CFLAGS += -DVCOS_VERIFY_BKPTS=1 -DUSE_VCHIQ_ARM -D__VCCOREVER__=0x04000000 -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel + + + +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/connections/connection.h +@@ -0,0 +1,309 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef CONNECTION_H_ ++#define CONNECTION_H_ ++ ++#include "interface/vchi/vchi_cfg_internal.h" ++#include "interface/vchi/vchi_common.h" ++#include "interface/vchi/message_drivers/message.h" ++ ++/****************************************************************************** ++ Global defs ++ *****************************************************************************/ ++ ++// Opaque handle for a connection / service pair ++typedef struct opaque_vchi_connection_connected_service_handle_t *VCHI_CONNECTION_SERVICE_HANDLE_T; ++ ++// opaque handle to the connection state information ++typedef struct opaque_vchi_connection_info_t VCHI_CONNECTION_STATE_T; ++ ++typedef struct vchi_connection_t VCHI_CONNECTION_T; ++ ++ ++/****************************************************************************** ++ API ++ *****************************************************************************/ ++ ++// Routine to init a connection with a particular low level driver ++typedef VCHI_CONNECTION_STATE_T * (*VCHI_CONNECTION_INIT_T)( struct vchi_connection_t * connection, ++ const VCHI_MESSAGE_DRIVER_T * driver ); ++ ++// Routine to control CRC enabling at a connection level ++typedef int32_t (*VCHI_CONNECTION_CRC_CONTROL_T)( VCHI_CONNECTION_STATE_T *state_handle, ++ VCHI_CRC_CONTROL_T control ); ++ ++// Routine to create a service ++typedef int32_t (*VCHI_CONNECTION_SERVICE_CONNECT_T)( VCHI_CONNECTION_STATE_T *state_handle, ++ vcos_fourcc_t service_id, ++ uint32_t rx_fifo_size, ++ uint32_t tx_fifo_size, ++ int server, ++ VCHI_CALLBACK_T callback, ++ void *callback_param, ++ vcos_bool_t want_crc, ++ vcos_bool_t want_unaligned_bulk_rx, ++ vcos_bool_t want_unaligned_bulk_tx, ++ VCHI_CONNECTION_SERVICE_HANDLE_T *service_handle ); ++ ++// Routine to close a service ++typedef int32_t (*VCHI_CONNECTION_SERVICE_DISCONNECT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle ); ++ ++// Routine to queue a message ++typedef int32_t (*VCHI_CONNECTION_SERVICE_QUEUE_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ const void *data, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// scatter-gather (vector) message queueing ++typedef int32_t (*VCHI_CONNECTION_SERVICE_QUEUE_MESSAGEV_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ VCHI_MSG_VECTOR_T *vector, ++ uint32_t count, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// Routine to dequeue a message ++typedef int32_t (*VCHI_CONNECTION_SERVICE_DEQUEUE_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void *data, ++ uint32_t max_data_size_to_read, ++ uint32_t *actual_msg_size, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to peek at a message ++typedef int32_t (*VCHI_CONNECTION_SERVICE_PEEK_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to hold a message ++typedef int32_t (*VCHI_CONNECTION_SERVICE_HOLD_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags, ++ void **message_handle ); ++ ++// Routine to initialise a received message iterator ++typedef int32_t (*VCHI_CONNECTION_SERVICE_LOOKAHEAD_MESSAGE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ VCHI_MSG_ITER_T *iter, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to release a held message ++typedef int32_t (*VCHI_CONNECTION_HELD_MSG_RELEASE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void *message_handle ); ++ ++// Routine to get info on a held message ++typedef int32_t (*VCHI_CONNECTION_HELD_MSG_INFO_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void *message_handle, ++ void **data, ++ int32_t *msg_size, ++ uint32_t *tx_timestamp, ++ uint32_t *rx_timestamp ); ++ ++// Routine to check whether the iterator has a next message ++typedef vcos_bool_t (*VCHI_CONNECTION_MSG_ITER_HAS_NEXT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, ++ const VCHI_MSG_ITER_T *iter ); ++ ++// Routine to advance the iterator ++typedef int32_t (*VCHI_CONNECTION_MSG_ITER_NEXT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, ++ VCHI_MSG_ITER_T *iter, ++ void **data, ++ uint32_t *msg_size ); ++ ++// Routine to remove the last message returned by the iterator ++typedef int32_t (*VCHI_CONNECTION_MSG_ITER_REMOVE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, ++ VCHI_MSG_ITER_T *iter ); ++ ++// Routine to hold the last message returned by the iterator ++typedef int32_t (*VCHI_CONNECTION_MSG_ITER_HOLD_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service, ++ VCHI_MSG_ITER_T *iter, ++ void **msg_handle ); ++ ++// Routine to transmit bulk data ++typedef int32_t (*VCHI_CONNECTION_BULK_QUEUE_TRANSMIT_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ const void *data_src, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *bulk_handle ); ++ ++// Routine to receive data ++typedef int32_t (*VCHI_CONNECTION_BULK_QUEUE_RECEIVE_T)( VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, ++ void *data_dst, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *bulk_handle ); ++ ++// Routine to report if a server is available ++typedef int32_t (*VCHI_CONNECTION_SERVER_PRESENT)( VCHI_CONNECTION_STATE_T *state, vcos_fourcc_t service_id, int32_t peer_flags ); ++ ++// Routine to report the number of RX slots available ++typedef int (*VCHI_CONNECTION_RX_SLOTS_AVAILABLE)( const VCHI_CONNECTION_STATE_T *state ); ++ ++// Routine to report the RX slot size ++typedef uint32_t (*VCHI_CONNECTION_RX_SLOT_SIZE)( const VCHI_CONNECTION_STATE_T *state ); ++ ++// Callback to indicate that the other side has added a buffer to the rx bulk DMA FIFO ++typedef void (*VCHI_CONNECTION_RX_BULK_BUFFER_ADDED)(VCHI_CONNECTION_STATE_T *state, ++ vcos_fourcc_t service, ++ uint32_t length, ++ MESSAGE_TX_CHANNEL_T channel, ++ uint32_t channel_params, ++ uint32_t data_length, ++ uint32_t data_offset); ++ ++// Callback to inform a service that a Xon or Xoff message has been received ++typedef void (*VCHI_CONNECTION_FLOW_CONTROL)(VCHI_CONNECTION_STATE_T *state, vcos_fourcc_t service_id, int32_t xoff); ++ ++// Callback to inform a service that a server available reply message has been received ++typedef void (*VCHI_CONNECTION_SERVER_AVAILABLE_REPLY)(VCHI_CONNECTION_STATE_T *state, vcos_fourcc_t service_id, uint32_t flags); ++ ++// Callback to indicate that bulk auxiliary messages have arrived ++typedef void (*VCHI_CONNECTION_BULK_AUX_RECEIVED)(VCHI_CONNECTION_STATE_T *state); ++ ++// Callback to indicate that bulk auxiliary messages have arrived ++typedef void (*VCHI_CONNECTION_BULK_AUX_TRANSMITTED)(VCHI_CONNECTION_STATE_T *state, void *handle); ++ ++// Callback with all the connection info you require ++typedef void (*VCHI_CONNECTION_INFO)(VCHI_CONNECTION_STATE_T *state, uint32_t protocol_version, uint32_t slot_size, uint32_t num_slots, uint32_t min_bulk_size); ++ ++// Callback to inform of a disconnect ++typedef void (*VCHI_CONNECTION_DISCONNECT)(VCHI_CONNECTION_STATE_T *state, uint32_t flags); ++ ++// Callback to inform of a power control request ++typedef void (*VCHI_CONNECTION_POWER_CONTROL)(VCHI_CONNECTION_STATE_T *state, MESSAGE_TX_CHANNEL_T channel, vcos_bool_t enable); ++ ++// allocate memory suitably aligned for this connection ++typedef void * (*VCHI_BUFFER_ALLOCATE)(VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, uint32_t * length); ++ ++// free memory allocated by buffer_allocate ++typedef void (*VCHI_BUFFER_FREE)(VCHI_CONNECTION_SERVICE_HANDLE_T service_handle, void * address); ++ ++ ++/****************************************************************************** ++ System driver struct ++ *****************************************************************************/ ++ ++struct opaque_vchi_connection_api_t ++{ ++ // Routine to init the connection ++ VCHI_CONNECTION_INIT_T init; ++ ++ // Connection-level CRC control ++ VCHI_CONNECTION_CRC_CONTROL_T crc_control; ++ ++ // Routine to connect to or create service ++ VCHI_CONNECTION_SERVICE_CONNECT_T service_connect; ++ ++ // Routine to disconnect from a service ++ VCHI_CONNECTION_SERVICE_DISCONNECT_T service_disconnect; ++ ++ // Routine to queue a message ++ VCHI_CONNECTION_SERVICE_QUEUE_MESSAGE_T service_queue_msg; ++ ++ // scatter-gather (vector) message queue ++ VCHI_CONNECTION_SERVICE_QUEUE_MESSAGEV_T service_queue_msgv; ++ ++ // Routine to dequeue a message ++ VCHI_CONNECTION_SERVICE_DEQUEUE_MESSAGE_T service_dequeue_msg; ++ ++ // Routine to peek at a message ++ VCHI_CONNECTION_SERVICE_PEEK_MESSAGE_T service_peek_msg; ++ ++ // Routine to hold a message ++ VCHI_CONNECTION_SERVICE_HOLD_MESSAGE_T service_hold_msg; ++ ++ // Routine to initialise a received message iterator ++ VCHI_CONNECTION_SERVICE_LOOKAHEAD_MESSAGE_T service_look_ahead_msg; ++ ++ // Routine to release a message ++ VCHI_CONNECTION_HELD_MSG_RELEASE_T held_msg_release; ++ ++ // Routine to get information on a held message ++ VCHI_CONNECTION_HELD_MSG_INFO_T held_msg_info; ++ ++ // Routine to check for next message on iterator ++ VCHI_CONNECTION_MSG_ITER_HAS_NEXT_T msg_iter_has_next; ++ ++ // Routine to get next message on iterator ++ VCHI_CONNECTION_MSG_ITER_NEXT_T msg_iter_next; ++ ++ // Routine to remove the last message returned by iterator ++ VCHI_CONNECTION_MSG_ITER_REMOVE_T msg_iter_remove; ++ ++ // Routine to hold the last message returned by iterator ++ VCHI_CONNECTION_MSG_ITER_HOLD_T msg_iter_hold; ++ ++ // Routine to transmit bulk data ++ VCHI_CONNECTION_BULK_QUEUE_TRANSMIT_T bulk_queue_transmit; ++ ++ // Routine to receive data ++ VCHI_CONNECTION_BULK_QUEUE_RECEIVE_T bulk_queue_receive; ++ ++ // Routine to report the available servers ++ VCHI_CONNECTION_SERVER_PRESENT server_present; ++ ++ // Routine to report the number of RX slots available ++ VCHI_CONNECTION_RX_SLOTS_AVAILABLE connection_rx_slots_available; ++ ++ // Routine to report the RX slot size ++ VCHI_CONNECTION_RX_SLOT_SIZE connection_rx_slot_size; ++ ++ // Callback to indicate that the other side has added a buffer to the rx bulk DMA FIFO ++ VCHI_CONNECTION_RX_BULK_BUFFER_ADDED rx_bulk_buffer_added; ++ ++ // Callback to inform a service that a Xon or Xoff message has been received ++ VCHI_CONNECTION_FLOW_CONTROL flow_control; ++ ++ // Callback to inform a service that a server available reply message has been received ++ VCHI_CONNECTION_SERVER_AVAILABLE_REPLY server_available_reply; ++ ++ // Callback to indicate that bulk auxiliary messages have arrived ++ VCHI_CONNECTION_BULK_AUX_RECEIVED bulk_aux_received; ++ ++ // Callback to indicate that a bulk auxiliary message has been transmitted ++ VCHI_CONNECTION_BULK_AUX_TRANSMITTED bulk_aux_transmitted; ++ ++ // Callback to provide information about the connection ++ VCHI_CONNECTION_INFO connection_info; ++ ++ // Callback to notify that peer has requested disconnect ++ VCHI_CONNECTION_DISCONNECT disconnect; ++ ++ // Callback to notify that peer has requested power change ++ VCHI_CONNECTION_POWER_CONTROL power_control; ++ ++ // allocate memory suitably aligned for this connection ++ VCHI_BUFFER_ALLOCATE buffer_allocate; ++ ++ // free memory allocated by buffer_allocate ++ VCHI_BUFFER_FREE buffer_free; ++ ++}; ++ ++struct vchi_connection_t { ++ const VCHI_CONNECTION_API_T *api; ++ VCHI_CONNECTION_STATE_T *state; ++#ifdef VCHI_COARSE_LOCKING ++ VCOS_SEMAPHORE_T sem; ++#endif ++}; ++ ++ ++#endif /* CONNECTION_H_ */ ++ ++/****************************** End of file **********************************/ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/message_drivers/message.h +@@ -0,0 +1,186 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef _VCHI_MESSAGE_H_ ++#define _VCHI_MESSAGE_H_ ++ ++#include "interface/vchi/vchi_cfg_internal.h" ++#include "interface/vcos/vcos.h" ++#include "interface/vchi/vchi_common.h" ++ ++ ++typedef enum message_event_type { ++ MESSAGE_EVENT_NONE, ++ MESSAGE_EVENT_NOP, ++ MESSAGE_EVENT_MESSAGE, ++ MESSAGE_EVENT_SLOT_COMPLETE, ++ MESSAGE_EVENT_RX_BULK_PAUSED, ++ MESSAGE_EVENT_RX_BULK_COMPLETE, ++ MESSAGE_EVENT_TX_COMPLETE, ++ MESSAGE_EVENT_MSG_DISCARDED ++} MESSAGE_EVENT_TYPE_T; ++ ++typedef enum vchi_msg_flags ++{ ++ VCHI_MSG_FLAGS_NONE = 0x0, ++ VCHI_MSG_FLAGS_TERMINATE_DMA = 0x1 ++} VCHI_MSG_FLAGS_T; ++ ++typedef enum message_tx_channel ++{ ++ MESSAGE_TX_CHANNEL_MESSAGE = 0, ++ MESSAGE_TX_CHANNEL_BULK = 1 // drivers may provide multiple bulk channels, from 1 upwards ++} MESSAGE_TX_CHANNEL_T; ++ ++// Macros used for cycling through bulk channels ++#define MESSAGE_TX_CHANNEL_BULK_PREV(c) (MESSAGE_TX_CHANNEL_BULK+((c)-MESSAGE_TX_CHANNEL_BULK+VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION-1)%VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION) ++#define MESSAGE_TX_CHANNEL_BULK_NEXT(c) (MESSAGE_TX_CHANNEL_BULK+((c)-MESSAGE_TX_CHANNEL_BULK+1)%VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION) ++ ++typedef enum message_rx_channel ++{ ++ MESSAGE_RX_CHANNEL_MESSAGE = 0, ++ MESSAGE_RX_CHANNEL_BULK = 1 // drivers may provide multiple bulk channels, from 1 upwards ++} MESSAGE_RX_CHANNEL_T; ++ ++// Message receive slot information ++typedef struct rx_msg_slot_info { ++ ++ struct rx_msg_slot_info *next; ++ //struct slot_info *prev; ++#if !defined VCHI_COARSE_LOCKING ++ VCOS_SEMAPHORE_T sem; ++#endif ++ ++ uint8_t *addr; // base address of slot ++ uint32_t len; // length of slot in bytes ++ ++ uint32_t write_ptr; // hardware causes this to advance ++ uint32_t read_ptr; // this module does the reading ++ int active; // is this slot in the hardware dma fifo? ++ uint32_t msgs_parsed; // count how many messages are in this slot ++ uint32_t msgs_released; // how many messages have been released ++ void *state; // connection state information ++ uint8_t ref_count[VCHI_MAX_SERVICES_PER_CONNECTION]; // reference count for slots held by services ++} RX_MSG_SLOTINFO_T; ++ ++// The message driver no longer needs to know about the fields of RX_BULK_SLOTINFO_T - sort this out. ++// In particular, it mustn't use addr and len - they're the client buffer, but the message ++// driver will be tasked with sending the aligned core section. ++typedef struct rx_bulk_slotinfo_t { ++ struct rx_bulk_slotinfo_t *next; ++ ++ VCOS_SEMAPHORE_T *blocking; ++ ++ // needed by DMA ++ void *addr; ++ uint32_t len; ++ ++ // needed for the callback ++ void *service; ++ void *handle; ++ VCHI_FLAGS_T flags; ++} RX_BULK_SLOTINFO_T; ++ ++ ++/* ---------------------------------------------------------------------- ++ * each connection driver will have a pool of the following struct. ++ * ++ * the pool will be managed by vchi_qman_* ++ * this means there will be multiple queues (single linked lists) ++ * a given struct message_info will be on exactly one of these queues ++ * at any one time ++ * -------------------------------------------------------------------- */ ++typedef struct rx_message_info { ++ ++ struct message_info *next; ++ //struct message_info *prev; ++ ++ uint8_t *addr; ++ uint32_t len; ++ RX_MSG_SLOTINFO_T *slot; // points to whichever slot contains this message ++ uint32_t tx_timestamp; ++ uint32_t rx_timestamp; ++ ++} RX_MESSAGE_INFO_T; ++ ++typedef struct { ++ MESSAGE_EVENT_TYPE_T type; ++ ++ struct { ++ // for messages ++ void *addr; // address of message ++ uint16_t slot_delta; // whether this message indicated slot delta ++ uint32_t len; // length of message ++ RX_MSG_SLOTINFO_T *slot; // slot this message is in ++ vcos_fourcc_t service; // service id this message is destined for ++ uint32_t tx_timestamp; // timestamp from the header ++ uint32_t rx_timestamp; // timestamp when we parsed it ++ } message; ++ ++ // FIXME: cleanup slot reporting... ++ RX_MSG_SLOTINFO_T *rx_msg; ++ RX_BULK_SLOTINFO_T *rx_bulk; ++ void *tx_handle; ++ MESSAGE_TX_CHANNEL_T tx_channel; ++ ++} MESSAGE_EVENT_T; ++ ++ ++// callbacks ++typedef void VCHI_MESSAGE_DRIVER_EVENT_CALLBACK_T( void *state ); ++ ++typedef struct { ++ VCHI_MESSAGE_DRIVER_EVENT_CALLBACK_T *event_callback; ++} VCHI_MESSAGE_DRIVER_OPEN_T; ++ ++ ++// handle to this instance of message driver (as returned by ->open) ++typedef struct opaque_mhandle_t *VCHI_MDRIVER_HANDLE_T; ++ ++struct opaque_vchi_message_driver_t { ++ VCHI_MDRIVER_HANDLE_T *(*open)( VCHI_MESSAGE_DRIVER_OPEN_T *params, void *state ); ++ int32_t (*suspending)( VCHI_MDRIVER_HANDLE_T *handle ); ++ int32_t (*resumed)( VCHI_MDRIVER_HANDLE_T *handle ); ++ int32_t (*power_control)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T, vcos_bool_t enable ); ++ int32_t (*add_msg_rx_slot)( VCHI_MDRIVER_HANDLE_T *handle, RX_MSG_SLOTINFO_T *slot ); // rx message ++ int32_t (*add_bulk_rx)( VCHI_MDRIVER_HANDLE_T *handle, void *data, uint32_t len, RX_BULK_SLOTINFO_T *slot ); // rx data (bulk) ++ int32_t (*send)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel, const void *data, uint32_t len, VCHI_MSG_FLAGS_T flags, void *send_handle ); // tx (message & bulk) ++ void (*next_event)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_EVENT_T *event ); // get the next event from message_driver ++ int32_t (*enable)( VCHI_MDRIVER_HANDLE_T *handle ); ++ int32_t (*form_message)( VCHI_MDRIVER_HANDLE_T *handle, vcos_fourcc_t service_id, VCHI_MSG_VECTOR_T *vector, uint32_t count, void ++ *address, uint32_t length_avail, uint32_t max_total_length, vcos_bool_t pad_to_fill, vcos_bool_t allow_partial ); ++ ++ int32_t (*update_message)( VCHI_MDRIVER_HANDLE_T *handle, void *dest, int16_t *slot_count ); ++ int32_t (*buffer_aligned)( VCHI_MDRIVER_HANDLE_T *handle, int tx, int uncached, const void *address, const uint32_t length ); ++ void * (*allocate_buffer)( VCHI_MDRIVER_HANDLE_T *handle, uint32_t *length ); ++ void (*free_buffer)( VCHI_MDRIVER_HANDLE_T *handle, void *address ); ++ int (*rx_slot_size)( VCHI_MDRIVER_HANDLE_T *handle, int msg_size ); ++ int (*tx_slot_size)( VCHI_MDRIVER_HANDLE_T *handle, int msg_size ); ++ ++ vcos_bool_t (*tx_supports_terminate)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); ++ uint32_t (*tx_bulk_chunk_size)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); ++ int (*tx_alignment)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel ); ++ int (*rx_alignment)( const VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_RX_CHANNEL_T channel ); ++ void (*form_bulk_aux)( VCHI_MDRIVER_HANDLE_T *handle, MESSAGE_TX_CHANNEL_T channel, const void *data, uint32_t len, uint32_t chunk_size, const void **aux_data, int32_t *aux_len ); ++ void (*debug)( VCHI_MDRIVER_HANDLE_T *handle ); ++}; ++ ++ ++#endif // _VCHI_MESSAGE_H_ ++ ++/****************************** End of file ***********************************/ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi.h +@@ -0,0 +1,347 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/*============================================================================= ++Contains the protypes for the vchi functions. ++=============================================================================*/ ++ ++#ifndef VCHI_H_ ++#define VCHI_H_ ++ ++#include "interface/vcos/vcos.h" ++#include "interface/vchi/vchi_cfg.h" ++#include "interface/vchi/vchi_common.h" ++#include "interface/vchi/connections/connection.h" ++#include "vchi_mh.h" ++ ++ ++/****************************************************************************** ++ Global defs ++ *****************************************************************************/ ++ ++#define VCHI_BULK_ROUND_UP(x) ((((unsigned long)(x))+VCHI_BULK_ALIGN-1) & ~(VCHI_BULK_ALIGN-1)) ++#define VCHI_BULK_ROUND_DOWN(x) (((unsigned long)(x)) & ~(VCHI_BULK_ALIGN-1)) ++#define VCHI_BULK_ALIGN_NBYTES(x) (VCHI_BULK_ALIGNED(x) ? 0 : (VCHI_BULK_ALIGN - ((unsigned long)(x) & (VCHI_BULK_ALIGN-1)))) ++ ++#ifdef USE_VCHIQ_ARM ++#define VCHI_BULK_ALIGNED(x) 1 ++#else ++#define VCHI_BULK_ALIGNED(x) (((unsigned long)(x) & (VCHI_BULK_ALIGN-1)) == 0) ++#endif ++ ++ ++typedef enum ++{ ++ VCHI_VEC_POINTER, ++ VCHI_VEC_HANDLE, ++ VCHI_VEC_LIST ++} VCHI_MSG_VECTOR_TYPE_T; ++ ++typedef struct vchi_msg_vector_ex { ++ ++ VCHI_MSG_VECTOR_TYPE_T type; ++ union ++ { ++ // a memory handle ++ struct ++ { ++ VCHI_MEM_HANDLE_T handle; ++ uint32_t offset; ++ int32_t vec_len; ++ } handle; ++ ++ // an ordinary data pointer ++ struct ++ { ++ const void *vec_base; ++ int32_t vec_len; ++ } ptr; ++ ++ // a nested vector list ++ struct ++ { ++ struct vchi_msg_vector_ex *vec; ++ uint32_t vec_len; ++ } list; ++ } u; ++} VCHI_MSG_VECTOR_EX_T; ++ ++ ++// Construct an entry in a msg vector for a pointer (p) of length (l) ++#define VCHI_VEC_POINTER(p,l) VCHI_VEC_POINTER, { { (VCHI_MEM_HANDLE_T)(p), (l) } } ++ ++// Construct an entry in a msg vector for a message handle (h), starting at offset (o) of length (l) ++#define VCHI_VEC_HANDLE(h,o,l) VCHI_VEC_HANDLE, { { (h), (o), (l) } } ++ ++// Macros to manipulate fourcc_t values ++#define MAKE_FOURCC(x) ((fourcc_t)( (x[0] << 24) | (x[1] << 16) | (x[2] << 8) | x[3] )) ++#define FOURCC_TO_CHAR(x) (x >> 24) & 0xFF,(x >> 16) & 0xFF,(x >> 8) & 0xFF, x & 0xFF ++ ++ ++// Opaque service information ++struct opaque_vchi_service_t; ++ ++// Descriptor for a held message. Allocated by client, initialised by vchi_msg_hold, ++// vchi_msg_iter_hold or vchi_msg_iter_hold_next. Fields are for internal VCHI use only. ++typedef struct ++{ ++ struct opaque_vchi_service_t *service; ++ void *message; ++} VCHI_HELD_MSG_T; ++ ++ ++ ++// structure used to provide the information needed to open a server or a client ++typedef struct { ++ vcos_fourcc_t service_id; ++ VCHI_CONNECTION_T *connection; ++ uint32_t rx_fifo_size; ++ uint32_t tx_fifo_size; ++ VCHI_CALLBACK_T callback; ++ void *callback_param; ++ vcos_bool_t want_unaligned_bulk_rx; // client intends to receive bulk transfers of odd lengths or into unaligned buffers ++ vcos_bool_t want_unaligned_bulk_tx; // client intends to transmit bulk transfers of odd lengths or out of unaligned buffers ++ vcos_bool_t want_crc; // client wants to check CRCs on (bulk) transfers. Only needs to be set at 1 end - will do both directions. ++} SERVICE_CREATION_T; ++ ++// Opaque handle for a VCHI instance ++typedef struct opaque_vchi_instance_handle_t *VCHI_INSTANCE_T; ++ ++// Opaque handle for a server or client ++typedef struct opaque_vchi_service_handle_t *VCHI_SERVICE_HANDLE_T; ++ ++// Service registration & startup ++typedef void (*VCHI_SERVICE_INIT)(VCHI_INSTANCE_T initialise_instance, VCHI_CONNECTION_T **connections, uint32_t num_connections); ++ ++typedef struct service_info_tag { ++ const char * const vll_filename; /* VLL to load to start this service. This is an empty string if VLL is "static" */ ++ VCHI_SERVICE_INIT init; /* Service initialisation function */ ++ void *vll_handle; /* VLL handle; NULL when unloaded or a "static VLL" in build */ ++} SERVICE_INFO_T; ++ ++/****************************************************************************** ++ Global funcs - implementation is specific to which side you are on (local / remote) ++ *****************************************************************************/ ++ ++#ifdef __cplusplus ++extern "C" { ++#endif ++ ++extern /*@observer@*/ VCHI_CONNECTION_T * vchi_create_connection( const VCHI_CONNECTION_API_T * function_table, ++ const VCHI_MESSAGE_DRIVER_T * low_level); ++ ++ ++// Routine used to initialise the vchi on both local + remote connections ++extern int32_t vchi_initialise( VCHI_INSTANCE_T *instance_handle ); ++ ++extern int32_t vchi_exit( void ); ++ ++extern int32_t vchi_connect( VCHI_CONNECTION_T **connections, ++ const uint32_t num_connections, ++ VCHI_INSTANCE_T instance_handle ); ++ ++//When this is called, ensure that all services have no data pending. ++//Bulk transfers can remain 'queued' ++extern int32_t vchi_disconnect( VCHI_INSTANCE_T instance_handle ); ++ ++// Global control over bulk CRC checking ++extern int32_t vchi_crc_control( VCHI_CONNECTION_T *connection, ++ VCHI_CRC_CONTROL_T control ); ++ ++// helper functions ++extern void * vchi_allocate_buffer(VCHI_SERVICE_HANDLE_T handle, uint32_t *length); ++extern void vchi_free_buffer(VCHI_SERVICE_HANDLE_T handle, void *address); ++extern uint32_t vchi_current_time(VCHI_INSTANCE_T instance_handle); ++ ++ ++/****************************************************************************** ++ Global service API ++ *****************************************************************************/ ++// Routine to create a named service ++extern int32_t vchi_service_create( VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, ++ VCHI_SERVICE_HANDLE_T *handle ); ++ ++// Routine to destory a service ++extern int32_t vchi_service_destroy( const VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to open a named service ++extern int32_t vchi_service_open( VCHI_INSTANCE_T instance_handle, ++ SERVICE_CREATION_T *setup, ++ VCHI_SERVICE_HANDLE_T *handle); ++ ++// Routine to close a named service ++extern int32_t vchi_service_close( const VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to increment ref count on a named service ++extern int32_t vchi_service_use( const VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to decrement ref count on a named service ++extern int32_t vchi_service_release( const VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to send a message accross a service ++extern int32_t vchi_msg_queue( VCHI_SERVICE_HANDLE_T handle, ++ const void *data, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// scatter-gather (vector) and send message ++int32_t vchi_msg_queuev_ex( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_VECTOR_EX_T *vector, ++ uint32_t count, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// legacy scatter-gather (vector) and send message, only handles pointers ++int32_t vchi_msg_queuev( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_VECTOR_T *vector, ++ uint32_t count, ++ VCHI_FLAGS_T flags, ++ void *msg_handle ); ++ ++// Routine to receive a msg from a service ++// Dequeue is equivalent to hold, copy into client buffer, release ++extern int32_t vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T handle, ++ void *data, ++ uint32_t max_data_size_to_read, ++ uint32_t *actual_msg_size, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to look at a message in place. ++// The message is not dequeued, so a subsequent call to peek or dequeue ++// will return the same message. ++extern int32_t vchi_msg_peek( VCHI_SERVICE_HANDLE_T handle, ++ void **data, ++ uint32_t *msg_size, ++ VCHI_FLAGS_T flags ); ++ ++// Routine to remove a message after it has been read in place with peek ++// The first message on the queue is dequeued. ++extern int32_t vchi_msg_remove( VCHI_SERVICE_HANDLE_T handle ); ++ ++// Routine to look at a message in place. ++// The message is dequeued, so the caller is left holding it; the descriptor is ++// filled in and must be released when the user has finished with the message. ++extern int32_t vchi_msg_hold( VCHI_SERVICE_HANDLE_T handle, ++ void **data, // } may be NULL, as info can be ++ uint32_t *msg_size, // } obtained from HELD_MSG_T ++ VCHI_FLAGS_T flags, ++ VCHI_HELD_MSG_T *message_descriptor ); ++ ++// Initialise an iterator to look through messages in place ++extern int32_t vchi_msg_look_ahead( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MSG_ITER_T *iter, ++ VCHI_FLAGS_T flags ); ++ ++/****************************************************************************** ++ Global service support API - operations on held messages and message iterators ++ *****************************************************************************/ ++ ++// Routine to get the address of a held message ++extern void *vchi_held_msg_ptr( const VCHI_HELD_MSG_T *message ); ++ ++// Routine to get the size of a held message ++extern int32_t vchi_held_msg_size( const VCHI_HELD_MSG_T *message ); ++ ++// Routine to get the transmit timestamp as written into the header by the peer ++extern uint32_t vchi_held_msg_tx_timestamp( const VCHI_HELD_MSG_T *message ); ++ ++// Routine to get the reception timestamp, written as we parsed the header ++extern uint32_t vchi_held_msg_rx_timestamp( const VCHI_HELD_MSG_T *message ); ++ ++// Routine to release a held message after it has been processed ++extern int32_t vchi_held_msg_release( VCHI_HELD_MSG_T *message ); ++ ++// Indicates whether the iterator has a next message. ++extern vcos_bool_t vchi_msg_iter_has_next( const VCHI_MSG_ITER_T *iter ); ++ ++// Return the pointer and length for the next message and advance the iterator. ++extern int32_t vchi_msg_iter_next( VCHI_MSG_ITER_T *iter, ++ void **data, ++ uint32_t *msg_size ); ++ ++// Remove the last message returned by vchi_msg_iter_next. ++// Can only be called once after each call to vchi_msg_iter_next. ++extern int32_t vchi_msg_iter_remove( VCHI_MSG_ITER_T *iter ); ++ ++// Hold the last message returned by vchi_msg_iter_next. ++// Can only be called once after each call to vchi_msg_iter_next. ++extern int32_t vchi_msg_iter_hold( VCHI_MSG_ITER_T *iter, ++ VCHI_HELD_MSG_T *message ); ++ ++// Return information for the next message, and hold it, advancing the iterator. ++extern int32_t vchi_msg_iter_hold_next( VCHI_MSG_ITER_T *iter, ++ void **data, // } may be NULL ++ uint32_t *msg_size, // } ++ VCHI_HELD_MSG_T *message ); ++ ++ ++/****************************************************************************** ++ Global bulk API ++ *****************************************************************************/ ++ ++// Routine to prepare interface for a transfer from the other side ++extern int32_t vchi_bulk_queue_receive( VCHI_SERVICE_HANDLE_T handle, ++ void *data_dst, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *transfer_handle ); ++ ++ ++// Prepare interface for a transfer from the other side into relocatable memory. ++int32_t vchi_bulk_queue_receive_reloc( const VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T h_dst, ++ uint32_t offset, ++ uint32_t data_size, ++ const VCHI_FLAGS_T flags, ++ void * const bulk_handle ); ++ ++// Routine to queue up data ready for transfer to the other (once they have signalled they are ready) ++extern int32_t vchi_bulk_queue_transmit( VCHI_SERVICE_HANDLE_T handle, ++ const void *data_src, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *transfer_handle ); ++ ++ ++/****************************************************************************** ++ Configuration plumbing ++ *****************************************************************************/ ++ ++// function prototypes for the different mid layers (the state info gives the different physical connections) ++extern const VCHI_CONNECTION_API_T *single_get_func_table( void ); ++//extern const VCHI_CONNECTION_API_T *local_server_get_func_table( void ); ++//extern const VCHI_CONNECTION_API_T *local_client_get_func_table( void ); ++ ++// declare all message drivers here ++const VCHI_MESSAGE_DRIVER_T *vchi_mphi_message_driver_func_table( void ); ++ ++#ifdef __cplusplus ++} ++#endif ++ ++extern int32_t vchi_bulk_queue_transmit_reloc( VCHI_SERVICE_HANDLE_T handle, ++ VCHI_MEM_HANDLE_T h_src, ++ uint32_t offset, ++ uint32_t data_size, ++ VCHI_FLAGS_T flags, ++ void *transfer_handle ); ++#endif /* VCHI_H_ */ ++ ++/****************************** End of file **********************************/ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi_cfg.h +@@ -0,0 +1,214 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/*============================================================================= ++Contains the #defines for the number of servers / clients etc, these can be ++over-ridden from the platform makefile if needed ++=============================================================================*/ ++ ++#ifndef VCHI_CFG_H_ ++#define VCHI_CFG_H_ ++ ++/**************************************************************************************** ++ * Defines in this first section are part of the VCHI API and may be examined by VCHI ++ * services. ++ ***************************************************************************************/ ++ ++/* Required alignment of base addresses for bulk transfer, if unaligned transfers are not enabled */ ++/* Really determined by the message driver, and should be available from a run-time call. */ ++#ifndef VCHI_BULK_ALIGN ++# if __VCCOREVER__ >= 0x04000000 ++# define VCHI_BULK_ALIGN 32 // Allows for the need to do cache cleans ++# else ++# define VCHI_BULK_ALIGN 16 ++# endif ++#endif ++ ++/* Required length multiple for bulk transfers, if unaligned transfers are not enabled */ ++/* May be less than or greater than VCHI_BULK_ALIGN */ ++/* Really determined by the message driver, and should be available from a run-time call. */ ++#ifndef VCHI_BULK_GRANULARITY ++# if __VCCOREVER__ >= 0x04000000 ++# define VCHI_BULK_GRANULARITY 32 // Allows for the need to do cache cleans ++# else ++# define VCHI_BULK_GRANULARITY 16 ++# endif ++#endif ++ ++/* The largest possible message to be queued with vchi_msg_queue. */ ++#ifndef VCHI_MAX_MSG_SIZE ++# if defined VCHI_LOCAL_HOST_PORT ++# define VCHI_MAX_MSG_SIZE 16384 // makes file transfers fast, but should they be using bulk? ++# else ++# define VCHI_MAX_MSG_SIZE 4096 // NOTE: THIS MUST BE LARGER THAN OR EQUAL TO THE SIZE OF THE KHRONOS MERGE BUFFER!! ++# endif ++#endif ++ ++/****************************************************************************************** ++ * Defines below are system configuration options, and should not be used by VCHI services. ++ *****************************************************************************************/ ++ ++/* How many connections can we support? A localhost implementation uses 2 connections, ++ * 1 for host-app, 1 for VMCS, and these are hooked together by a loopback MPHI VCFW ++ * driver. */ ++#ifndef VCHI_MAX_NUM_CONNECTIONS ++# define VCHI_MAX_NUM_CONNECTIONS 3 ++#endif ++ ++/* How many services can we open per connection? Extending this doesn't cost processing time, just a small ++ * amount of static memory. */ ++#ifndef VCHI_MAX_SERVICES_PER_CONNECTION ++# define VCHI_MAX_SERVICES_PER_CONNECTION 36 ++#endif ++ ++/* Adjust if using a message driver that supports more logical TX channels */ ++#ifndef VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION ++# define VCHI_MAX_BULK_TX_CHANNELS_PER_CONNECTION 9 // 1 MPHI + 8 CCP2 logical channels ++#endif ++ ++/* Adjust if using a message driver that supports more logical RX channels */ ++#ifndef VCHI_MAX_BULK_RX_CHANNELS_PER_CONNECTION ++# define VCHI_MAX_BULK_RX_CHANNELS_PER_CONNECTION 1 // 1 MPHI ++#endif ++ ++/* How many receive slots do we use. This times VCHI_MAX_MSG_SIZE gives the effective ++ * receive queue space, less message headers. */ ++#ifndef VCHI_NUM_READ_SLOTS ++# if defined(VCHI_LOCAL_HOST_PORT) ++# define VCHI_NUM_READ_SLOTS 4 ++# else ++# define VCHI_NUM_READ_SLOTS 48 ++# endif ++#endif ++ ++/* Do we utilise overrun facility for receive message slots? Can aid peer transmit ++ * performance. Only define on VideoCore end, talking to host. ++ */ ++//#define VCHI_MSG_RX_OVERRUN ++ ++/* How many transmit slots do we use. Generally don't need many, as the hardware driver ++ * underneath VCHI will usually have its own buffering. */ ++#ifndef VCHI_NUM_WRITE_SLOTS ++# define VCHI_NUM_WRITE_SLOTS 4 ++#endif ++ ++/* If a service has held or queued received messages in VCHI_XOFF_THRESHOLD or more slots, ++ * then it's taking up too much buffer space, and the peer service will be told to stop ++ * transmitting with an XOFF message. For this to be effective, the VCHI_NUM_READ_SLOTS ++ * needs to be considerably bigger than VCHI_NUM_WRITE_SLOTS, or the transmit latency ++ * is too high. */ ++#ifndef VCHI_XOFF_THRESHOLD ++# define VCHI_XOFF_THRESHOLD (VCHI_NUM_READ_SLOTS / 2) ++#endif ++ ++/* After we've sent an XOFF, the peer will be told to resume transmission once the local ++ * service has dequeued/released enough messages that it's now occupying ++ * VCHI_XON_THRESHOLD slots or fewer. */ ++#ifndef VCHI_XON_THRESHOLD ++# define VCHI_XON_THRESHOLD (VCHI_NUM_READ_SLOTS / 4) ++#endif ++ ++/* A size below which a bulk transfer omits the handshake completely and always goes ++ * via the message channel, if bulk auxiliary is being sent on that service. (The user ++ * can guarantee this by enabling unaligned transmits). ++ * Not API. */ ++#ifndef VCHI_MIN_BULK_SIZE ++# define VCHI_MIN_BULK_SIZE ( VCHI_MAX_MSG_SIZE / 2 < 4096 ? VCHI_MAX_MSG_SIZE / 2 : 4096 ) ++#endif ++ ++/* Maximum size of bulk transmission chunks, for each interface type. A trade-off between ++ * speed and latency; the smaller the chunk size the better change of messages and other ++ * bulk transmissions getting in when big bulk transfers are happening. Set to 0 to not ++ * break transmissions into chunks. ++ */ ++#ifndef VCHI_MAX_BULK_CHUNK_SIZE_MPHI ++# define VCHI_MAX_BULK_CHUNK_SIZE_MPHI (16 * 1024) ++#endif ++ ++/* NB Chunked CCP2 transmissions violate the letter of the CCP2 spec by using "JPEG8" mode ++ * with multiple-line frames. Only use if the receiver can cope. */ ++#ifndef VCHI_MAX_BULK_CHUNK_SIZE_CCP2 ++# define VCHI_MAX_BULK_CHUNK_SIZE_CCP2 0 ++#endif ++ ++/* How many TX messages can we have pending in our transmit slots. Once exhausted, ++ * vchi_msg_queue will be blocked. */ ++#ifndef VCHI_TX_MSG_QUEUE_SIZE ++# define VCHI_TX_MSG_QUEUE_SIZE 256 ++#endif ++ ++/* How many RX messages can we have parsed in the receive slots. Once exhausted, parsing ++ * will be suspended until older messages are dequeued/released. */ ++#ifndef VCHI_RX_MSG_QUEUE_SIZE ++# define VCHI_RX_MSG_QUEUE_SIZE 256 ++#endif ++ ++/* Really should be able to cope if we run out of received message descriptors, by ++ * suspending parsing as the comment above says, but we don't. This sweeps the issue ++ * under the carpet. */ ++#if VCHI_RX_MSG_QUEUE_SIZE < (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS ++# undef VCHI_RX_MSG_QUEUE_SIZE ++# define VCHI_RX_MSG_QUEUE_SIZE (VCHI_MAX_MSG_SIZE/16 + 1) * VCHI_NUM_READ_SLOTS ++#endif ++ ++/* How many bulk transmits can we have pending. Once exhausted, vchi_bulk_queue_transmit ++ * will be blocked. */ ++#ifndef VCHI_TX_BULK_QUEUE_SIZE ++# define VCHI_TX_BULK_QUEUE_SIZE 64 ++#endif ++ ++/* How many bulk receives can we have pending. Once exhausted, vchi_bulk_queue_receive ++ * will be blocked. */ ++#ifndef VCHI_RX_BULK_QUEUE_SIZE ++# define VCHI_RX_BULK_QUEUE_SIZE 64 ++#endif ++ ++/* A limit on how many outstanding bulk requests we expect the peer to give us. If ++ * the peer asks for more than this, VCHI will fail and assert. The number is determined ++ * by the peer's hardware - it's the number of outstanding requests that can be queued ++ * on all bulk channels. VC3's MPHI peripheral allows 16. */ ++#ifndef VCHI_MAX_PEER_BULK_REQUESTS ++# define VCHI_MAX_PEER_BULK_REQUESTS 32 ++#endif ++ ++/* Define VCHI_CCP2TX_MANUAL_POWER if the host tells us when to turn the CCP2 ++ * transmitter on and off. ++ */ ++/*#define VCHI_CCP2TX_MANUAL_POWER*/ ++ ++#ifndef VCHI_CCP2TX_MANUAL_POWER ++ ++/* Timeout (in milliseconds) for putting the CCP2TX interface into IDLE state. Set ++ * negative for no IDLE. ++ */ ++# ifndef VCHI_CCP2TX_IDLE_TIMEOUT ++# define VCHI_CCP2TX_IDLE_TIMEOUT 5 ++# endif ++ ++/* Timeout (in milliseconds) for putting the CCP2TX interface into OFF state. Set ++ * negative for no OFF. ++ */ ++# ifndef VCHI_CCP2TX_OFF_TIMEOUT ++# define VCHI_CCP2TX_OFF_TIMEOUT 1000 ++# endif ++ ++#endif /* VCHI_CCP2TX_MANUAL_POWER */ ++ ++#endif /* VCHI_CFG_H_ */ ++ ++/****************************** End of file **********************************/ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi_cfg_internal.h +@@ -0,0 +1,56 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#ifndef VCHI_CFG_INTERNAL_H_ ++#define VCHI_CFG_INTERNAL_H_ ++ ++/**************************************************************************************** ++ * Control optimisation attempts. ++ ***************************************************************************************/ ++ ++// Don't use lots of short-term locks - use great long ones, reducing the overall locks-per-second ++#define VCHI_COARSE_LOCKING ++ ++// Avoid lock then unlock on exit from blocking queue operations (msg tx, bulk rx/tx) ++// (only relevant if VCHI_COARSE_LOCKING) ++#define VCHI_ELIDE_BLOCK_EXIT_LOCK ++ ++// Avoid lock on non-blocking peek ++// (only relevant if VCHI_COARSE_LOCKING) ++#define VCHI_AVOID_PEEK_LOCK ++ ++// Use one slot-handler thread per connection, rather than 1 thread dealing with all connections in rotation. ++#define VCHI_MULTIPLE_HANDLER_THREADS ++ ++// Put free descriptors onto the head of the free queue, rather than the tail, so that we don't thrash ++// our way through the pool of descriptors. ++#define VCHI_PUSH_FREE_DESCRIPTORS_ONTO_HEAD ++ ++// Don't issue a MSG_AVAILABLE callback for every single message. Possibly only safe if VCHI_COARSE_LOCKING. ++#define VCHI_FEWER_MSG_AVAILABLE_CALLBACKS ++ ++// Don't use message descriptors for TX messages that don't need them ++#define VCHI_MINIMISE_TX_MSG_DESCRIPTORS ++ ++// Nano-locks for multiqueue ++//#define VCHI_MQUEUE_NANOLOCKS ++ ++// Lock-free(er) dequeuing ++//#define VCHI_RX_NANOLOCKS ++ ++#endif /*VCHI_CFG_INTERNAL_H_*/ +--- /dev/null ++++ b/drivers/misc/vc04_services/interface/vchi/vchi_common.h +@@ -0,0 +1,152 @@ ++/* ++ * Copyright (c) 2010-2011 Broadcom Corporation. All rights reserved. ++ * ++ * 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 program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++/*============================================================================= ++Contains global defs used by submodules within vchi. ++=============================================================================*/ ++ ++#ifndef VCHI_COMMON_H_ ++#define VCHI_COMMON_H_ ++ ++ ++//flags used when sending messages (must be bitmapped) ++typedef enum ++{ ++ VCHI_FLAGS_NONE = 0x0, ++ VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE = 0x1, // waits for message to be received, or sent (NB. not the same as being seen on other side) ++ VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE = 0x2, // run a callback when message sent ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED = 0x4, // return once the transfer is in a queue ready to go ++ VCHI_FLAGS_ALLOW_PARTIAL = 0x8, ++ VCHI_FLAGS_BLOCK_UNTIL_DATA_READ = 0x10, ++ VCHI_FLAGS_CALLBACK_WHEN_DATA_READ = 0x20, ++ ++ VCHI_FLAGS_ALIGN_SLOT = 0x000080, // internal use only ++ VCHI_FLAGS_BULK_AUX_QUEUED = 0x010000, // internal use only ++ VCHI_FLAGS_BULK_AUX_COMPLETE = 0x020000, // internal use only ++ VCHI_FLAGS_BULK_DATA_QUEUED = 0x040000, // internal use only ++ VCHI_FLAGS_BULK_DATA_COMPLETE = 0x080000, // internal use only ++ VCHI_FLAGS_INTERNAL = 0xFF0000 ++} VCHI_FLAGS_T; ++ ++// constants for vchi_crc_control() ++typedef enum { ++ VCHI_CRC_NOTHING = -1, ++ VCHI_CRC_PER_SERVICE = 0, ++ VCHI_CRC_EVERYTHING = 1, ++} VCHI_CRC_CONTROL_T; ++ ++//callback reasons when an event occurs on a service ++typedef enum ++{ ++ VCHI_CALLBACK_REASON_MIN, ++ ++ //This indicates that there is data available ++ //handle is the msg id that was transmitted with the data ++ // When a message is received and there was no FULL message available previously, send callback ++ // Tasks get kicked by the callback, reset their event and try and read from the fifo until it fails ++ VCHI_CALLBACK_MSG_AVAILABLE, ++ VCHI_CALLBACK_MSG_SENT, ++ VCHI_CALLBACK_MSG_SPACE_AVAILABLE, // XXX not yet implemented ++ ++ // This indicates that a transfer from the other side has completed ++ VCHI_CALLBACK_BULK_RECEIVED, ++ //This indicates that data queued up to be sent has now gone ++ //handle is the msg id that was used when sending the data ++ VCHI_CALLBACK_BULK_SENT, ++ VCHI_CALLBACK_BULK_RX_SPACE_AVAILABLE, // XXX not yet implemented ++ VCHI_CALLBACK_BULK_TX_SPACE_AVAILABLE, // XXX not yet implemented ++ ++ VCHI_CALLBACK_SERVICE_CLOSED, ++ ++ // this side has sent XOFF to peer due to lack of data consumption by service ++ // (suggests the service may need to take some recovery action if it has ++ // been deliberately holding off consuming data) ++ VCHI_CALLBACK_SENT_XOFF, ++ VCHI_CALLBACK_SENT_XON, ++ ++ // indicates that a bulk transfer has finished reading the source buffer ++ VCHI_CALLBACK_BULK_DATA_READ, ++ ++ // power notification events (currently host side only) ++ VCHI_CALLBACK_PEER_OFF, ++ VCHI_CALLBACK_PEER_SUSPENDED, ++ VCHI_CALLBACK_PEER_ON, ++ VCHI_CALLBACK_PEER_RESUMED, ++ VCHI_CALLBACK_FORCED_POWER_OFF, ++ ++#ifdef USE_VCHIQ_ARM ++ // some extra notifications provided by vchiq_arm ++ VCHI_CALLBACK_SERVICE_OPENED, ++ VCHI_CALLBACK_BULK_RECEIVE_ABORTED, ++ VCHI_CALLBACK_BULK_TRANSMIT_ABORTED, ++#endif ++ ++ VCHI_CALLBACK_REASON_MAX ++} VCHI_CALLBACK_REASON_T; ++ ++//Calback used by all services / bulk transfers ++typedef void (*VCHI_CALLBACK_T)( void *callback_param, //my service local param ++ VCHI_CALLBACK_REASON_T reason, ++ void *handle ); //for transmitting msg's only ++ ++ ++ ++/* ++ * Define vector struct for scatter-gather (vector) operations ++ * Vectors can be nested - if a vector element has negative length, then ++ * the data pointer is treated as pointing to another vector array, with ++ * '-vec_len' elements. Thus to append a header onto an existing vector, ++ * you can do this: ++ * ++ * void foo(const VCHI_MSG_VECTOR_T *v, int n) ++ * { ++ * VCHI_MSG_VECTOR_T nv[2]; ++ * nv[0].vec_base = my_header; ++ * nv[0].vec_len = sizeof my_header; ++ * nv[1].vec_base = v; ++ * nv[1].vec_len = -n; ++ * ... ++ * ++ */ ++typedef struct vchi_msg_vector { ++ const void *vec_base; ++ int32_t vec_len; ++} VCHI_MSG_VECTOR_T; ++ ++// Opaque type for a connection API ++typedef struct opaque_vchi_connection_api_t VCHI_CONNECTION_API_T; ++ ++// Opaque type for a message driver ++typedef struct opaque_vchi_message_driver_t VCHI_MESSAGE_DRIVER_T; ++ ++ ++// Iterator structure for reading ahead through received message queue. Allocated by client, ++// initialised by vchi_msg_look_ahead. Fields are for internal VCHI use only. ++// Iterates over messages in queue at the instant of the call to vchi_msg_lookahead - ++// will not proceed to messages received since. Behaviour is undefined if an iterator ++// is used again after messages for that service are removed/dequeued by any ++// means other than vchi_msg_iter_... calls on the iterator itself. ++typedef struct { ++ struct opaque_vchi_service_t *service; ++ void *last; ++ void *next; ++ void *remove; ++} VCHI_MSG_ITER_T; ++ ++ ++#endif // VCHI_COMMON_H_ +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_2835_arm.c +@@ -36,7 +36,7 @@ + #define TOTAL_SLOTS (VCHIQ_SLOT_ZERO_SLOTS + 2 * 32) + + #define VCHIQ_DOORBELL_IRQ IRQ_ARM_DOORBELL_0 +-#define VCHIQ_ARM_ADDRESS(x) __virt_to_bus(x) ++#define VCHIQ_ARM_ADDRESS(x) ((void *)__virt_to_bus((unsigned)x)) + + #include "vchiq_arm.h" + #include "vchiq_2835.h" +@@ -182,7 +182,15 @@ remote_event_signal(REMOTE_EVENT_T *even + int + vchiq_copy_from_user(void *dst, const void *src, int size) + { +- return copy_from_user(dst, src, size); ++ if ( (uint32_t)src < TASK_SIZE) ++ { ++ return copy_from_user(dst, src, size); ++ } ++ else ++ { ++ memcpy( dst, src, size ); ++ return 0; ++ } + } + + VCHIQ_STATUS_T +@@ -239,6 +247,22 @@ vchiq_dump_platform_state(void *dump_con + vchiq_dump(dump_context, buf, len + 1); + } + ++VCHIQ_STATUS_T ++vchiq_platform_suspend(VCHIQ_STATE_T *state) ++{ ++ vcos_unused(state); ++ vcos_assert_msg(0, "Suspend/resume not supported"); ++ return VCHIQ_ERROR; ++} ++ ++VCHIQ_STATUS_T ++vchiq_platform_resume(VCHIQ_STATE_T *state) ++{ ++ vcos_unused(state); ++ vcos_assert_msg(0, "Suspend/resume not supported"); ++ return VCHIQ_ERROR; ++} ++ + void + vchiq_platform_paused(VCHIQ_STATE_T *state) + { +@@ -253,33 +277,40 @@ vchiq_platform_resumed(VCHIQ_STATE_T *st + vcos_assert_msg(0, "Suspend/resume not supported"); + } + +-VCHIQ_STATUS_T +-vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle) ++int ++vchiq_platform_videocore_wanted(VCHIQ_STATE_T* state) + { +- VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; +- if (!service) +- return VCHIQ_ERROR; +- return VCHIQ_SUCCESS; ++ vcos_unused(state); ++ return 1; // autosuspend not supported - videocore always wanted + } + +-VCHIQ_STATUS_T +-vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle) ++#if VCOS_HAVE_TIMER ++int ++vchiq_platform_use_suspend_timer(void) + { +- VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; +- if (!service) +- return VCHIQ_ERROR; +- return VCHIQ_SUCCESS; ++ return 0; ++} ++#endif ++void ++vchiq_dump_platform_use_state(VCHIQ_STATE_T *state) ++{ ++ vcos_unused(state); + } + + VCHIQ_STATUS_T +-vchiq_check_service(VCHIQ_SERVICE_HANDLE_T handle) ++vchiq_platform_init_state(VCHIQ_STATE_T *state) + { +- VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; +- if (!service) +- return VCHIQ_ERROR; ++ vcos_unused(state); + return VCHIQ_SUCCESS; + } + ++VCHIQ_ARM_STATE_T* ++vchiq_platform_get_arm_state(VCHIQ_STATE_T *state) ++{ ++ vcos_unused(state); ++ return NULL; ++} ++ + /* + * Local functions + */ +@@ -479,9 +510,3 @@ free_pagelist(PAGELIST_T *pagelist, int + kfree(pagelist); + } + +-VCHIQ_STATUS_T +-vchiq_platform_suspend(VCHIQ_STATE_T *state) +-{ +- vcos_unused(state); +- return VCHIQ_ERROR; +-} +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.c +@@ -23,6 +23,9 @@ + #include <linux/cdev.h> + #include <linux/fs.h> + #include <linux/device.h> ++#include <linux/mm.h> ++#include <linux/highmem.h> ++#include <linux/pagemap.h> + + #include "vchiq_core.h" + #include "vchiq_ioctl.h" +@@ -44,6 +47,15 @@ + + #define VCOS_LOG_CATEGORY (&vchiq_arm_log_category) + ++#define VCHIQ_ARM_VCSUSPEND_TASK_STACK 4096 ++ ++#if VCOS_HAVE_TIMER ++#define SUSPEND_TIMER_TIMEOUT_MS 100 ++static VCOS_TIMER_T g_suspend_timer; ++static void suspend_timer_callback(void *context); ++#endif ++ ++ + typedef struct client_service_struct { + VCHIQ_SERVICE_T *service; + void *userdata; +@@ -106,10 +118,17 @@ static const char *ioctl_names[] = + "GET_CONFIG", + "CLOSE_SERVICE", + "USE_SERVICE", +- "RELEASE_SERIVCE" ++ "RELEASE_SERVICE", ++ "SET_SERVICE_OPTION", ++ "DUMP_PHYS_MEM" + }; + +-VCOS_LOG_LEVEL_T vchiq_default_arm_log_level = VCOS_LOG_WARN; ++vcos_static_assert(vcos_countof(ioctl_names) == (VCHIQ_IOC_MAX + 1)); ++ ++VCOS_LOG_LEVEL_T vchiq_default_arm_log_level = VCOS_LOG_ERROR; ++ ++static void ++dump_phys_mem( void *virt_addr, uint32_t num_bytes ); + + /**************************************************************************** + * +@@ -118,7 +137,7 @@ VCOS_LOG_LEVEL_T vchiq_default_arm_log_l + ***************************************************************************/ + + static inline USER_SERVICE_T *find_service_by_handle( +- VCHIQ_INSTANCE_T instance, int handle ) ++ VCHIQ_INSTANCE_T instance, int handle ) + { + USER_SERVICE_T *user_service; + +@@ -524,7 +543,7 @@ vchiq_ioctl(struct file *file, unsigned + status = (cmd == VCHIQ_IOC_USE_SERVICE) ? vchiq_use_service(&user_service->service->base) : vchiq_release_service(&user_service->service->base); + if (status != VCHIQ_SUCCESS) + { +- ret = -EINVAL; // ??? ++ ret = -EINVAL; /* ??? */ + } + } + } +@@ -872,6 +891,21 @@ vchiq_ioctl(struct file *file, unsigned + } + break; + ++ case VCHIQ_IOC_DUMP_PHYS_MEM: ++ { ++ VCHIQ_DUMP_MEM_T args; ++ ++ if (copy_from_user ++ (&args, (const void __user *)arg, ++ sizeof(args)) != 0) { ++ ret = -EFAULT; ++ break; ++ } ++ dump_phys_mem( args.virt_addr, args.num_bytes ); ++ } ++ break; ++ ++ + default: + ret = -ENOTTY; + break; +@@ -1060,7 +1094,7 @@ vchiq_dump(void *dump_context, const cha + char cr = '\n'; + if (copy_to_user(context->buf + context->actual - 1, &cr, 1)) + { +- context->actual = -EFAULT; ++ context->actual = -EFAULT; + } + } + } +@@ -1153,6 +1187,88 @@ vchiq_dump_platform_service_state(void * + + /**************************************************************************** + * ++* dump_user_mem ++* ++***************************************************************************/ ++ ++static void ++dump_phys_mem( void *virt_addr, uint32_t num_bytes ) ++{ ++ int rc; ++ uint8_t *end_virt_addr = virt_addr + num_bytes; ++ int num_pages; ++ int offset; ++ int end_offset; ++ int page_idx; ++ int prev_idx; ++ struct page *page; ++ struct page **pages; ++ uint8_t *kmapped_virt_ptr; ++ ++ // Align virtAddr and endVirtAddr to 16 byte boundaries. ++ ++ virt_addr = (void *)((unsigned long)virt_addr & ~0x0fuL ); ++ end_virt_addr = (void *)(( (unsigned long)end_virt_addr + 15uL ) & ~0x0fuL); ++ ++ offset = (int)(long)virt_addr & ( PAGE_SIZE - 1 ); ++ end_offset = (int)(long)end_virt_addr & ( PAGE_SIZE - 1 ); ++ ++ num_pages = (offset + num_bytes + PAGE_SIZE - 1) / PAGE_SIZE; ++ ++ if (( pages = kmalloc( sizeof( struct page *) * num_pages, GFP_KERNEL )) == NULL ) ++ { ++ printk( KERN_ERR "Unable to allocation memory for %d pages\n", num_pages ); ++ return; ++ } ++ ++ down_read( ¤t->mm->mmap_sem ); ++ rc = get_user_pages( current, /* task */ ++ current->mm, /* mm */ ++ (unsigned long)virt_addr, /* start */ ++ num_pages, /* len */ ++ 0, /* write */ ++ 0, /* force */ ++ pages, /* pages (array of pointers to page) */ ++ NULL ); /* vmas */ ++ up_read( ¤t->mm->mmap_sem ); ++ ++ prev_idx = -1; ++ page = NULL; ++ ++ while ( offset < end_offset ) { ++ ++ int page_offset = offset % PAGE_SIZE; ++ page_idx = offset / PAGE_SIZE; ++ ++ if ( page_idx != prev_idx ) { ++ ++ if (page != NULL) { ++ kunmap( page ); ++ } ++ page = pages[page_idx]; ++ kmapped_virt_ptr = kmap( page ); ++ ++ prev_idx = page_idx; ++ } ++ ++ vcos_log_dump_mem_impl( &vchiq_arm_log_category, "ph", ++ (uint32_t)(unsigned long)&kmapped_virt_ptr[page_offset], ++ &kmapped_virt_ptr[page_offset], 16 ); ++ ++ offset += 16; ++ } ++ if (page != NULL) { ++ kunmap( page ); ++ } ++ ++ for ( page_idx = 0; page_idx < num_pages; page_idx++ ) { ++ page_cache_release( pages[page_idx] ); ++ } ++ kfree( pages ); ++} ++ ++/**************************************************************************** ++* + * vchiq_read + * + ***************************************************************************/ +@@ -1204,6 +1320,505 @@ vchiq_fops = { + .read = vchiq_read + }; + ++/* ++ * Autosuspend related functionality ++ */ ++ ++static int vchiq_videocore_wanted(VCHIQ_STATE_T* state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ if(!arm_state) ++ { // autosuspend not supported - always return wanted ++ return 1; ++ } ++ else if(!arm_state->videocore_use_count) ++ { // usage count zero - check for override ++ return vchiq_platform_videocore_wanted(state); ++ } ++ else ++ { // non-zero usage count - videocore still required ++ return 1; ++ } ++} ++ ++ ++/* Called by the lp thread */ ++static void * ++lp_func(void *v) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ ++ while (1) { ++ vcos_event_wait(&arm_state->lp_evt); ++ ++ vcos_mutex_lock(&arm_state->use_count_mutex); ++ if (!vchiq_videocore_wanted(state)) ++ { ++ arm_state->suspend_pending = 1; ++ } ++ vcos_mutex_unlock(&arm_state->use_count_mutex); ++ ++ vchiq_arm_vcsuspend(state); ++ } ++ return NULL; ++} ++/* Called by the hp thread */ ++static void * ++hp_func(void *v) ++{ ++ VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ int send_pending; ++ ++ while (1) { ++ vcos_event_wait(&arm_state->hp_evt); ++ ++ send_pending = 0; ++ ++ vcos_mutex_lock(&arm_state->use_count_mutex); ++ if (vchiq_videocore_wanted(state)) ++ { ++ vchiq_arm_vcresume(state); ++ } ++ if(arm_state->use_notify_pending) ++ { ++ send_pending = arm_state->use_notify_pending; ++ arm_state->use_notify_pending=0; ++ } ++ vcos_mutex_unlock(&arm_state->use_count_mutex); ++ while(send_pending--) ++ { ++ vcos_log_info( "%s sending VCHIQ_MSG_REMOTE_USE_ACTIVE", __func__); ++ if ( vchiq_send_remote_use_active(state) != VCHIQ_SUCCESS) ++ { ++ BUG(); /* vc should be resumed, so shouldn't be a problem sending message */ ++ } ++ } ++ } ++ return NULL; ++} ++ ++VCHIQ_STATUS_T ++vchiq_arm_init_state(VCHIQ_STATE_T* state, VCHIQ_ARM_STATE_T *arm_state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ VCOS_THREAD_ATTR_T attrs; ++ char threadname[10]; ++ ++ if(arm_state) ++ { ++ vcos_mutex_create(&arm_state->use_count_mutex, "v.use_count_mutex"); ++ vcos_mutex_create(&arm_state->suspend_resume_mutex, "v.susp_res_mutex"); ++ ++ vcos_event_create(&arm_state->lp_evt, "LP_EVT"); ++ vcos_event_create(&arm_state->hp_evt, "HP_EVT"); ++ ++ vcos_thread_attr_init(&attrs); ++ vcos_thread_attr_setstacksize(&attrs, VCHIQ_ARM_VCSUSPEND_TASK_STACK); ++ vcos_thread_attr_setpriority(&attrs, VCOS_THREAD_PRI_LOWEST); ++ vcos_snprintf(threadname, sizeof(threadname), "VCHIQl-%d", state->id); ++ if(vcos_thread_create(&arm_state->lp_thread, threadname, &attrs, lp_func, state) != VCOS_SUCCESS) ++ { ++ vcos_log_error("vchiq: FATAL: couldn't create thread %s", threadname); ++ status = VCHIQ_ERROR; ++ } ++ else ++ { ++ vcos_thread_attr_init(&attrs); ++ vcos_thread_attr_setstacksize(&attrs, VCHIQ_ARM_VCSUSPEND_TASK_STACK); ++ vcos_thread_attr_setpriority(&attrs, VCOS_THREAD_PRI_HIGHEST); ++ vcos_snprintf(threadname, sizeof(threadname), "VCHIQh-%d", state->id); ++ ++ if(vcos_thread_create(&arm_state->hp_thread, threadname, &attrs, hp_func, state) != VCOS_SUCCESS) ++ { ++ vcos_log_error("vchiq: FATAL: couldn't create thread %s", threadname); ++ status = VCHIQ_ERROR; ++ } ++ } ++ } ++ return status; ++} ++ ++ ++VCHIQ_STATUS_T ++vchiq_arm_vcsuspend(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ ++ if (state->conn_state != VCHIQ_CONNSTATE_CONNECTED) ++ return VCHIQ_ERROR; ++ ++ if(arm_state->suspend_pending) ++ { ++ vcos_mutex_lock(&arm_state->suspend_resume_mutex); ++ if(arm_state->videocore_suspended) ++ { ++ vcos_log_info("%s - already suspended", __func__); ++ } ++ else ++ { ++ vcos_log_info("%s - suspending", __func__); ++ ++ status = vchiq_platform_suspend(state); ++ arm_state->videocore_suspended = (status == VCHIQ_SUCCESS) ? 1 : 0; ++ ++ vcos_mutex_unlock(&arm_state->suspend_resume_mutex); ++ ++ vcos_mutex_lock(&arm_state->use_count_mutex); ++ if(!arm_state->suspend_pending) ++ { /* Something has changed the suspend_pending state while we were suspending. ++ Run the HP task to check if we need to resume */ ++ vcos_log_info( "%s trigger HP task to check resume", __func__); ++ vcos_event_signal(&arm_state->hp_evt); ++ } ++ arm_state->suspend_pending = 0; ++ vcos_mutex_unlock(&arm_state->use_count_mutex); ++ } ++ } ++ else ++ { ++ vchiq_check_resume(state); ++ } ++ return status; ++} ++ ++ ++VCHIQ_STATUS_T ++vchiq_arm_vcresume(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_SUCCESS; ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ vcos_mutex_lock(&arm_state->suspend_resume_mutex); ++ ++ status = vchiq_platform_resume(state); ++ arm_state->videocore_suspended = (status == VCHIQ_RETRY) ? 1 : 0; ++ ++ vcos_mutex_unlock(&arm_state->suspend_resume_mutex); ++ ++ return status; ++} ++ ++void ++vchiq_check_resume(VCHIQ_STATE_T* state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ vcos_mutex_lock(&arm_state->use_count_mutex); ++ ++ if (arm_state->videocore_suspended && vchiq_videocore_wanted(state)) ++ { /* signal high priority task to resume vc */ ++ vcos_event_signal(&arm_state->hp_evt); ++ } ++ ++ vcos_mutex_unlock(&arm_state->use_count_mutex); ++} ++ ++void ++vchiq_check_suspend(VCHIQ_STATE_T* state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ ++ vcos_mutex_lock(&arm_state->use_count_mutex); ++ ++ if (!arm_state->videocore_suspended && !vchiq_videocore_wanted(state)) ++ { /* signal low priority task to suspend vc */ ++ vcos_event_signal(&arm_state->lp_evt); ++ } ++ ++ vcos_mutex_unlock(&arm_state->use_count_mutex); ++} ++ ++ ++ ++static VCHIQ_STATUS_T ++vchiq_use_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service, int block_while_resume) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ VCHIQ_STATUS_T ret = VCHIQ_SUCCESS; ++ char entity[10]; ++ int* entity_uc; ++ ++ if(arm_state) ++ { ++ vcos_mutex_lock(&arm_state->use_count_mutex); ++ ++ if (service) ++ { ++ sprintf(entity, "%c%c%c%c:%03d",VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), service->client_id); ++ entity_uc = &service->service_use_count; ++ } ++ else ++ { ++ sprintf(entity, "PEER: "); ++ entity_uc = &arm_state->peer_use_count; ++ } ++ ++ if (!arm_state->videocore_suspended && !vchiq_videocore_wanted(state)) ++ { ++#if VCOS_HAVE_TIMER ++ if (vchiq_platform_use_suspend_timer()) ++ { ++ vcos_log_trace( "%s %s - cancel suspend timer", __func__, entity); ++ } ++ vcos_timer_cancel(&g_suspend_timer); ++#endif ++ } ++ ++ arm_state->videocore_use_count++; ++ (*entity_uc)++; ++ arm_state->suspend_pending = 0; ++ ++ if (arm_state->videocore_suspended && vchiq_videocore_wanted(state)) ++ { ++ vcos_log_info( "%s %s count %d, state count %d", __func__, entity, *entity_uc, arm_state->videocore_use_count); ++ if(block_while_resume) ++ { ++ ret = vchiq_arm_vcresume(state); ++ } ++ else ++ { ++ vcos_log_info( "%s trigger HP task to do resume", __func__); /* triggering is done below */ ++ } ++ } ++ else ++ { ++ vcos_log_trace( "%s %s count %d, state count %d", __func__, entity, *entity_uc, arm_state->videocore_use_count); ++ } ++ if(!block_while_resume) ++ { ++ arm_state->use_notify_pending++; ++ vcos_event_signal(&arm_state->hp_evt); /* hp task will check if we need to resume and also send use notify */ ++ } ++ ++ if (ret == VCHIQ_RETRY) ++ { /* if we're told to retry, decrement the counters. VCHIQ_ERROR probably means we're already resumed. */ ++ (*entity_uc)--; ++ arm_state->videocore_use_count--; ++ } ++ ++ vcos_mutex_unlock(&arm_state->use_count_mutex); ++ } ++ return ret; ++} ++ ++static VCHIQ_STATUS_T ++vchiq_release_internal(VCHIQ_STATE_T *state, VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ VCHIQ_STATUS_T ret = VCHIQ_SUCCESS; ++ char entity[10]; ++ int* entity_uc; ++ ++ if(arm_state) ++ { ++ vcos_mutex_lock(&arm_state->use_count_mutex); ++ ++ if (service) ++ { ++ sprintf(entity, "%c%c%c%c:%03d",VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), service->client_id); ++ entity_uc = &service->service_use_count; ++ } ++ else ++ { ++ sprintf(entity, "PEER: "); ++ entity_uc = &arm_state->peer_use_count; ++ } ++ ++ if (*entity_uc && arm_state->videocore_use_count) ++ { ++ arm_state->videocore_use_count--; ++ (*entity_uc)--; ++ ++ if (!vchiq_videocore_wanted(state)) ++ { ++#if VCOS_HAVE_TIMER ++ if (vchiq_platform_use_suspend_timer()) ++ { ++ vcos_log_trace( "%s %s count %d, state count %d - starting suspend timer", __func__, entity, *entity_uc, arm_state->videocore_use_count); ++ vcos_timer_cancel(&g_suspend_timer); ++ vcos_timer_set(&g_suspend_timer, SUSPEND_TIMER_TIMEOUT_MS); ++ } ++ else ++#endif ++ { ++ vcos_log_info( "%s %s count %d, state count %d - suspend pending", __func__, entity, *entity_uc, arm_state->videocore_use_count); ++ vcos_event_signal(&arm_state->lp_evt); /* kick the lp thread to do the suspend */ ++ } ++ } ++ else ++ { ++ vcos_log_trace( "%s %s count %d, state count %d", __func__, entity, *entity_uc, arm_state->videocore_use_count); ++ } ++ } ++ else ++ { ++ vcos_log_error( "%s %s ERROR releasing service; count %d, state count %d", __func__, entity, *entity_uc, arm_state->videocore_use_count); ++ ret = VCHIQ_ERROR; ++ } ++ ++ vcos_mutex_unlock(&arm_state->use_count_mutex); ++ } ++ return ret; ++} ++ ++VCHIQ_STATUS_T ++vchiq_on_remote_use(VCHIQ_STATE_T *state) ++{ ++ vcos_log_info("%s state %p", __func__, state); ++ return state ? vchiq_use_internal(state, NULL, 0) : VCHIQ_ERROR; ++} ++ ++VCHIQ_STATUS_T ++vchiq_on_remote_release(VCHIQ_STATE_T *state) ++{ ++ vcos_log_info("%s state %p", __func__, state); ++ return state ? vchiq_release_internal(state, NULL) : VCHIQ_ERROR; ++} ++ ++VCHIQ_STATUS_T ++vchiq_use_service_internal(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_STATE_T* state = NULL; ++ ++ if (service) ++ { ++ state = service->state; ++ } ++ ++ if (!service || !state) ++ { ++ return VCHIQ_ERROR; ++ } ++ return vchiq_use_internal(state, service, 1); ++} ++ ++VCHIQ_STATUS_T ++vchiq_release_service_internal(VCHIQ_SERVICE_T *service) ++{ ++ VCHIQ_STATE_T* state = NULL; ++ ++ if (service) ++ { ++ state = service->state; ++ } ++ ++ if (!service || !state) ++ { ++ return VCHIQ_ERROR; ++ } ++ return vchiq_release_internal(state, service); ++} ++ ++ ++#if VCOS_HAVE_TIMER ++static void suspend_timer_callback(void* context) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state((VCHIQ_STATE_T*)context); ++ vcos_log_info( "%s - suspend pending", __func__); ++ vcos_event_signal(&arm_state->lp_evt); ++} ++#endif ++ ++VCHIQ_STATUS_T ++vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_STATUS_T ret = VCHIQ_ERROR; ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *) handle; ++ if (service) ++ { ++ ret = vchiq_use_service_internal(service); ++ } ++ return ret; ++} ++ ++VCHIQ_STATUS_T ++vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle) ++{ ++ VCHIQ_STATUS_T ret = VCHIQ_ERROR; ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *) handle; ++ if (service) ++ { ++ ret = vchiq_release_service_internal(service); ++ } ++ return ret; ++} ++ ++void ++vchiq_dump_service_use_state(VCHIQ_STATE_T *state) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(state); ++ int i; ++ if(arm_state) ++ { ++ vcos_mutex_lock(&arm_state->suspend_resume_mutex); ++ if (arm_state->videocore_suspended) ++ { ++ vcos_log_warn("--VIDEOCORE SUSPENDED--"); ++ } ++ else ++ { ++ vcos_log_warn("--VIDEOCORE AWAKE--"); ++ } ++ for (i = 0; i < state->unused_service; i++) { ++ VCHIQ_SERVICE_T *service_ptr = state->services[i]; ++ if (service_ptr && (service_ptr->srvstate != VCHIQ_SRVSTATE_FREE)) ++ { ++ if (service_ptr->service_use_count) ++ vcos_log_error("----- %c%c%c%c:%d service count %d <-- preventing suspend", VCHIQ_FOURCC_AS_4CHARS(service_ptr->base.fourcc), service_ptr->client_id, service_ptr->service_use_count); ++ else ++ vcos_log_warn("----- %c%c%c%c:%d service count 0", VCHIQ_FOURCC_AS_4CHARS(service_ptr->base.fourcc), service_ptr->client_id); ++ } ++ } ++ vcos_log_warn("----- PEER use count count %d", arm_state->peer_use_count); ++ vcos_log_warn("--- Overall vchiq instance use count %d", arm_state->videocore_use_count); ++ ++ vchiq_dump_platform_use_state(state); ++ ++ vcos_mutex_unlock(&arm_state->suspend_resume_mutex); ++ } ++} ++ ++VCHIQ_STATUS_T ++vchiq_check_service(VCHIQ_SERVICE_T * service) ++{ ++ VCHIQ_ARM_STATE_T *arm_state = vchiq_platform_get_arm_state(service->state); ++ VCHIQ_STATUS_T ret = VCHIQ_ERROR; ++ /* on 2835 vchiq does not have an arm_state */ ++ if (!arm_state) ++ return VCHIQ_SUCCESS; ++ if (service && arm_state) ++ { ++ vcos_mutex_lock(&arm_state->use_count_mutex); ++ if (!service->service_use_count) ++ { ++ vcos_log_error( "%s ERROR - %c%c%c%c:%d service count %d, state count %d, videocore_suspended %d", __func__,VCHIQ_FOURCC_AS_4CHARS(service->base.fourcc), service->client_id, service->service_use_count, arm_state->videocore_use_count, arm_state->videocore_suspended); ++ vchiq_dump_service_use_state(service->state); ++ vcos_assert(0); // vcos_assert should kill the calling thread, so a user thread shouldn't be able to kill the kernel. ++ } ++ else ++ { ++ ret = VCHIQ_SUCCESS; ++ } ++ vcos_mutex_unlock(&arm_state->use_count_mutex); ++ } ++ return ret; ++} ++ ++/* stub functions */ ++void vchiq_on_remote_use_active(VCHIQ_STATE_T *state) ++{ ++ vcos_unused(state); ++} ++ ++void vchiq_platform_conn_state_changed(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate) ++{ ++ vcos_unused(state); ++ vcos_unused(oldstate); ++ vcos_unused(oldstate); ++} ++ ++ + /**************************************************************************** + * + * vchiq_init - called when the module is loaded. +@@ -1250,6 +1865,10 @@ vchiq_init(void) + if (err != 0) + goto failed_platform_init; + ++#if VCOS_HAVE_TIMER ++ vcos_timer_create( &g_suspend_timer, "suspend_timer", suspend_timer_callback, (void*)(&g_state)); ++#endif ++ + vcos_log_error("vchiq: initialised - version %d (min %d), device %d.%d", + VCHIQ_VERSION, VCHIQ_VERSION_MIN, + MAJOR(vchiq_devid), MINOR(vchiq_devid)); +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_arm.h +@@ -21,6 +21,40 @@ + + #include "vchiq_core.h" + ++ ++typedef struct vchiq_arm_state_struct { ++ ++ VCOS_THREAD_T lp_thread; /* processes low priority messages (eg suspend) */ ++ VCOS_THREAD_T hp_thread; /* processes high priority messages (eg resume) */ ++ ++ VCOS_EVENT_T lp_evt; ++ VCOS_EVENT_T hp_evt; ++ ++ VCOS_MUTEX_T use_count_mutex; ++ VCOS_MUTEX_T suspend_resume_mutex; ++ ++ int suspend_pending; ++ ++ /* Global use count for videocore. ++ * This is equal to the sum of the use counts for all services. When this hits ++ * zero the videocore suspend procedure will be initiated. */ ++ int videocore_use_count; ++ ++ /* Use count to track requests from videocore peer. ++ * This use count is not associated with a service, so needs to be tracked separately ++ * with the state. ++ */ ++ int peer_use_count; ++ ++ /* Flag to indicate whether videocore is currently suspended */ ++ int videocore_suspended; ++ ++ /* Flag to indicate whether a notification is pending back to videocore that it's ++ * "remote use request" has been actioned */ ++ int use_notify_pending; ++} VCHIQ_ARM_STATE_T; ++ ++ + extern VCOS_LOG_CAT_T vchiq_arm_log_category; + + extern int __init +@@ -35,4 +69,50 @@ vchiq_platform_exit(VCHIQ_STATE_T *state + extern VCHIQ_STATE_T * + vchiq_get_state(void); + ++extern VCHIQ_STATUS_T ++vchiq_arm_vcsuspend(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_arm_vcresume(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_arm_init_state(VCHIQ_STATE_T *state, VCHIQ_ARM_STATE_T *arm_state); ++ ++extern void ++vchiq_check_resume(VCHIQ_STATE_T* state); ++ ++extern void ++vchiq_check_suspend(VCHIQ_STATE_T* state); ++ ++extern VCHIQ_STATUS_T ++vchiq_use_service(VCHIQ_SERVICE_HANDLE_T handle); ++ ++extern VCHIQ_STATUS_T ++vchiq_release_service(VCHIQ_SERVICE_HANDLE_T handle); ++ ++extern VCHIQ_STATUS_T ++vchiq_check_service(VCHIQ_SERVICE_T * service); ++ ++extern VCHIQ_STATUS_T ++vchiq_platform_suspend(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_platform_resume(VCHIQ_STATE_T *state); ++ ++extern int ++vchiq_platform_videocore_wanted(VCHIQ_STATE_T* state); ++ ++extern int ++vchiq_platform_use_suspend_timer(void); ++ ++extern void ++vchiq_dump_platform_use_state(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_dump_service_use_state(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_ARM_STATE_T* ++vchiq_platform_get_arm_state(VCHIQ_STATE_T *state); ++ ++ + #endif /* VCHIQ_ARM_H */ +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.c +@@ -30,6 +30,11 @@ + + #define BULK_INDEX(x) (x & (VCHIQ_NUM_SERVICE_BULKS - 1)) + ++ ++/* Used to check use counts allow vchiq use. */ ++extern VCHIQ_STATUS_T vchiq_check_service(VCHIQ_SERVICE_T * service); ++ ++ + typedef struct bulk_waiter_struct + { + VCOS_EVENT_T event; +@@ -114,6 +119,13 @@ vchiq_set_service_state(VCHIQ_SERVICE_T + service->srvstate = newstate; + } + ++static inline int ++is_valid_service(VCHIQ_SERVICE_T *service) ++{ ++ return ((service != NULL) && ++ (service->srvstate != VCHIQ_SRVSTATE_FREE)); ++} ++ + static inline VCHIQ_STATUS_T + make_service_callback(VCHIQ_SERVICE_T *service, VCHIQ_REASON_T reason, + VCHIQ_HEADER_T *header, void *bulk_userdata) +@@ -127,10 +139,12 @@ make_service_callback(VCHIQ_SERVICE_T *s + static inline void + vchiq_set_conn_state(VCHIQ_STATE_T *state, VCHIQ_CONNSTATE_T newstate) + { ++ VCHIQ_CONNSTATE_T oldstate = state->conn_state; + vcos_log_info("%d: %s->%s", state->id, +- conn_state_names[state->conn_state], ++ conn_state_names[oldstate], + conn_state_names[newstate]); + state->conn_state = newstate; ++ vchiq_platform_conn_state_changed(state, oldstate, newstate); + } + + static inline void +@@ -323,7 +337,7 @@ process_free_queue(VCHIQ_STATE_T *state) + + while (slot_queue_available != local->slot_queue_recycle) + { +- int pos; ++ unsigned int pos; + int slot_index = local->slot_queue[slot_queue_available++ & VCHIQ_SLOT_QUEUE_MASK]; + char *data = (char *)SLOT_DATA_FROM_INDEX(state, slot_index); + +@@ -343,17 +357,37 @@ process_free_queue(VCHIQ_STATE_T *state) + if (VCHIQ_MSG_TYPE(msgid) == VCHIQ_MSG_DATA) + { + int port = VCHIQ_MSG_SRCPORT(msgid); ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &state->service_quotas[port]; ++ int count; ++ count = service_quota->message_use_count; ++ if (count > 0) ++ { ++ service_quota->message_use_count = count - 1; ++ if (count == service_quota->message_quota) ++ { ++ /* Signal the service that it has dropped below its quota */ ++ vcos_event_signal(&service_quota->quota_event); ++ } ++ } ++ else ++ { ++ vcos_log_error("service %d message_use_count=%d (header %x," ++ " msgid %x, header->msgid %x, header->size %x)", ++ port, service_quota->message_use_count, ++ (unsigned int)header, msgid, header->msgid, ++ header->size); ++ vcos_assert(0); ++ } + if (!BITSET_IS_SET(service_found, port)) + { +- VCHIQ_SERVICE_QUOTA_T *service_quota = +- &state->service_quotas[port]; +- + /* Set the found bit for this service */ + BITSET_SET(service_found, port); + +- if (service_quota->slot_use_count > 0) ++ count = service_quota->slot_use_count; ++ if (count > 0) + { +- service_quota->slot_use_count--; ++ service_quota->slot_use_count = count - 1; + /* Signal the service in case it has dropped below its quota */ + vcos_event_signal(&service_quota->quota_event); + vcos_log_trace("%d: pfq:%d %x@%x - slot_use->%d", +@@ -376,7 +410,7 @@ process_free_queue(VCHIQ_STATE_T *state) + pos += calc_stride(header->size); + if (pos > VCHIQ_SLOT_SIZE) + { +- vcos_log_error("pos %x: header %x, msgid %x, header->msgid %x, header->size %x", ++ vcos_log_error("pfq - pos %x: header %x, msgid %x, header->msgid %x, header->size %x", + pos, (unsigned int)header, msgid, header->msgid, header->size); + vcos_assert(0); + } +@@ -431,20 +465,21 @@ queue_message(VCHIQ_STATE_T *state, VCHI + + service_quota = &state->service_quotas[service->localport]; + +- /* ...ensure it doesn't use more than its quota of slots */ +- while ((tx_end_index != service_quota->previous_tx_index) && +- (service_quota->slot_use_count == service_quota->slot_quota)) ++ /* ...ensure it doesn't use more than its quota of messages or slots */ ++ while ((service_quota->message_use_count == service_quota->message_quota) || ++ ((tx_end_index != service_quota->previous_tx_index) && ++ (service_quota->slot_use_count == service_quota->slot_quota))) + { +- vcos_log_trace("%d: qm:%d %s,%x - quota stall", ++ vcos_log_trace("%d: qm:%d %s,%x - quota stall (msg %d, slot %d)", + state->id, service->localport, +- msg_type_str(VCHIQ_MSG_TYPE(msgid)), size); ++ msg_type_str(VCHIQ_MSG_TYPE(msgid)), size, ++ service_quota->message_use_count, service_quota->slot_use_count); + VCHIQ_SERVICE_STATS_INC(service, quota_stalls); + vcos_mutex_unlock(&state->slot_mutex); + if (vcos_event_wait(&service_quota->quota_event) != VCOS_SUCCESS) + return VCHIQ_RETRY; + if (vcos_mutex_lock(&state->slot_mutex) != VCOS_SUCCESS) + return VCHIQ_RETRY; +- vcos_assert(service_quota->slot_use_count <= service_quota->slot_quota); + tx_end_index = SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos + stride - 1); + } + } +@@ -498,6 +533,7 @@ queue_message(VCHIQ_STATE_T *state, VCHI + } + + service_quota->previous_tx_index = tx_end_index; ++ service_quota->message_use_count++; + VCHIQ_SERVICE_STATS_INC(service, ctrl_tx_count); + VCHIQ_SERVICE_STATS_ADD(service, ctrl_tx_bytes, size); + } else { +@@ -1232,6 +1268,17 @@ parse_rx_slots(VCHIQ_STATE_T *state) + vchiq_set_conn_state(state, VCHIQ_CONNSTATE_CONNECTED); + vchiq_platform_resumed(state); + break; ++ ++ case VCHIQ_MSG_REMOTE_USE: ++ vchiq_on_remote_use(state); ++ break; ++ case VCHIQ_MSG_REMOTE_RELEASE: ++ vchiq_on_remote_release(state); ++ break; ++ case VCHIQ_MSG_REMOTE_USE_ACTIVE: ++ vchiq_on_remote_use_active(state); ++ break; ++ + default: + vcos_log_error("%d: prs invalid msgid %x@%x,%x", + state->id, msgid, (unsigned int)header, size); +@@ -1326,8 +1373,6 @@ slot_handler_func(void *v) + return NULL; + } + +-extern VCHIQ_STATUS_T +-vchiq_platform_suspend(VCHIQ_STATE_T *state); + + /* Called by the recycle thread */ + static void * +@@ -1348,23 +1393,6 @@ recycle_func(void *v) + return NULL; + } + +-/* Called by the lp thread */ +-static void * +-lp_func(void *v) +-{ +- VCHIQ_STATE_T *state = (VCHIQ_STATE_T *) v; +- +- while (1) { +- vcos_event_wait(&state->lp_evt); +- vcos_mutex_lock(&state->use_count_mutex); +- if (state->videocore_use_count == 0) +- { +- vchiq_platform_suspend(state); +- } +- vcos_mutex_unlock(&state->use_count_mutex); +- } +- return NULL; +-} + + static void + init_bulk_queue(VCHIQ_BULK_QUEUE_T *queue) +@@ -1417,6 +1445,7 @@ vchiq_init_state(VCHIQ_STATE_T *state, V + VCHIQ_SHARED_STATE_T *local; + VCHIQ_SHARED_STATE_T *remote; + VCOS_THREAD_ATTR_T attrs; ++ VCHIQ_STATUS_T status; + char threadname[10]; + static int id = 0; + int i; +@@ -1426,7 +1455,7 @@ vchiq_init_state(VCHIQ_STATE_T *state, V + vcos_log_register("vchiq_core", &vchiq_core_log_category); + vcos_log_register("vchiq_core_msg", &vchiq_core_msg_log_category); + +- vcos_log_warn( "%s: slot_zero = 0x%08lx, is_master = %d\n", __func__, (unsigned long)slot_zero, is_master ); ++ vcos_log_warn( "%s: slot_zero = 0x%08lx, is_master = %d", __func__, (unsigned long)slot_zero, is_master ); + + /* Check the input configuration */ + +@@ -1501,6 +1530,7 @@ vchiq_init_state(VCHIQ_STATE_T *state, V + } + + memset(state, 0, sizeof(VCHIQ_STATE_T)); ++ vcos_log_warn( "%s: called", __func__); + state->id = id++; + state->is_master = is_master; + +@@ -1523,8 +1553,6 @@ vchiq_init_state(VCHIQ_STATE_T *state, V + + vcos_mutex_create(&state->slot_mutex, "v.slot_mutex"); + vcos_mutex_create(&state->recycle_mutex, "v.recycle_mutex"); +- vcos_mutex_create(&state->use_count_mutex, "v.use_count_mutex"); +- vcos_mutex_create(&state->suspend_resume_mutex, "v.susp_res_mutex"); + + vcos_event_create(&state->slot_available_event, "v.slot_available_event"); + vcos_event_create(&state->slot_remove_event, "v.slot_remove_event"); +@@ -1543,6 +1571,7 @@ vchiq_init_state(VCHIQ_STATE_T *state, V + } + + state->default_slot_quota = state->slot_queue_available/2; ++ state->default_message_quota = vcos_min(state->default_slot_quota * 256, (unsigned short)~0); + + local->trigger.event = &state->trigger_event; + remote_event_create(&local->trigger); +@@ -1552,8 +1581,6 @@ vchiq_init_state(VCHIQ_STATE_T *state, V + remote_event_create(&local->recycle); + local->slot_queue_recycle = state->slot_queue_available; + +- vcos_event_create(&state->lp_evt, "LP_EVT"); +- + local->debug[DEBUG_ENTRIES] = DEBUG_MAX; + + /* +@@ -1566,7 +1593,10 @@ vchiq_init_state(VCHIQ_STATE_T *state, V + vcos_snprintf(threadname, sizeof(threadname), "VCHIQ-%d", state->id); + if (vcos_thread_create(&state->slot_handler_thread, threadname, + &attrs, slot_handler_func, state) != VCOS_SUCCESS) ++ { ++ vcos_log_error("vchiq: FATAL: couldn't create thread %s", threadname); + return VCHIQ_ERROR; ++ } + + vcos_thread_attr_init(&attrs); + vcos_thread_attr_setstacksize(&attrs, VCHIQ_SLOT_HANDLER_STACK); +@@ -1574,20 +1604,17 @@ vchiq_init_state(VCHIQ_STATE_T *state, V + vcos_snprintf(threadname, sizeof(threadname), "VCHIQr-%d", state->id); + if (vcos_thread_create(&state->recycle_thread, threadname, + &attrs, recycle_func, state) != VCOS_SUCCESS) ++ { ++ vcos_log_error("vchiq: FATAL: couldn't create thread %s", threadname); + return VCHIQ_ERROR; ++ } + +- vcos_thread_attr_init(&attrs); +- vcos_thread_attr_setstacksize(&attrs, VCHIQ_SLOT_HANDLER_STACK); +- vcos_thread_attr_setpriority(&attrs, VCOS_THREAD_PRI_LOWEST); +- vcos_snprintf(threadname, sizeof(threadname), "VCHIQl-%d", state->id); +- if (vcos_thread_create(&state->lp_thread, threadname, +- &attrs, lp_func, state) != VCOS_SUCCESS) +- return VCHIQ_ERROR; ++ status = vchiq_platform_init_state(state); + + /* Indicate readiness to the other side */ + local->initialised = 1; + +- return VCHIQ_SUCCESS; ++ return status; + } + + /* Called from application thread when a client or server service is created. */ +@@ -1684,6 +1711,7 @@ vchiq_add_service_internal(VCHIQ_STATE_T + init_bulk_queue(&service->bulk_tx); + init_bulk_queue(&service->bulk_rx); + service_quota->slot_quota = state->default_slot_quota; ++ service_quota->message_quota = state->default_message_quota; + if (service_quota->slot_use_count == 0) + service_quota->previous_tx_index = + SLOT_QUEUE_INDEX_FROM_POS(state->local_tx_pos) - 1; +@@ -1833,9 +1861,13 @@ vchiq_close_service_internal(VCHIQ_SERVI + + if (service->srvstate == VCHIQ_SRVSTATE_CLOSING) + { ++ int i; ++ int uc = service->service_use_count; + /* Complete the close process */ +- vchiq_release_service(&service->base); +- ++ for( i=0; i<uc; i++) ++ { /* cater for cases where close is forced and the client may not close all it's handles */ ++ vchiq_release_service_internal(service); ++ } + service->client_id = 0; + + /* Now tell the client that the services is closed */ +@@ -1912,7 +1944,7 @@ vchiq_free_service_internal(VCHIQ_SERVIC + if (slot_info->release_count != slot_info->use_count) + { + char *data = (char *)SLOT_DATA_FROM_INDEX(state, i); +- int pos, end; ++ unsigned int pos, end; + + end = VCHIQ_SLOT_SIZE; + if (data == state->rx_data) +@@ -1938,6 +1970,12 @@ vchiq_free_service_internal(VCHIQ_SERVIC + } + } + pos += calc_stride(header->size); ++ if (pos > VCHIQ_SLOT_SIZE) ++ { ++ vcos_log_error("fsi - pos %x: header %x, msgid %x, header->msgid %x, header->size %x", ++ pos, (unsigned int)header, msgid, header->msgid, header->size); ++ vcos_assert(0); ++ } + } + } + } +@@ -2050,7 +2088,7 @@ vchiq_close_service(VCHIQ_SERVICE_HANDLE + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *) handle; + VCHIQ_STATUS_T status = VCHIQ_ERROR; + +- if (service == NULL) ++ if (!is_valid_service(service)) + return VCHIQ_ERROR; + + vcos_log_info("%d: close_service:%d", service->state->id, service->localport); +@@ -2080,7 +2118,7 @@ vchiq_remove_service(VCHIQ_SERVICE_HANDL + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *) handle; + VCHIQ_STATUS_T status = VCHIQ_SUCCESS; + +- if (service == NULL) ++ if (!is_valid_service(service)) + return VCHIQ_ERROR; + + vcos_log_info("%d: remove_service:%d", service->state->id, service->localport); +@@ -2137,15 +2175,14 @@ vchiq_bulk_transfer(VCHIQ_SERVICE_T *ser + const int dir_msgtype = (dir == VCHIQ_BULK_TRANSMIT) ? VCHIQ_MSG_BULK_TX : VCHIQ_MSG_BULK_RX; + VCHIQ_STATUS_T status = VCHIQ_ERROR; + +- if ((service == NULL) || +- ((memhandle == VCHI_MEM_HANDLE_INVALID) && (offset == NULL))) ++ if (!is_valid_service(service) || ++ (service->srvstate != VCHIQ_SRVSTATE_OPEN) || ++ ((memhandle == VCHI_MEM_HANDLE_INVALID) && (offset == NULL)) || ++ (vchiq_check_service(service) != VCHIQ_SUCCESS)) + return VCHIQ_ERROR; + + state = service->state; + +- if (service->srvstate != VCHIQ_SRVSTATE_OPEN) +- return VCHIQ_ERROR; /* Must be connected */ +- + if (vcos_mutex_lock(&service->bulk_mutex) != VCOS_SUCCESS) + return VCHIQ_RETRY; + +@@ -2325,8 +2362,9 @@ vchiq_queue_message(VCHIQ_SERVICE_HANDLE + unsigned int size = 0; + unsigned int i; + +- if ((service == NULL) || +- (service->srvstate != VCHIQ_SRVSTATE_OPEN)) ++ if (!is_valid_service(service) || ++ (service->srvstate != VCHIQ_SRVSTATE_OPEN) || ++ (vchiq_check_service(service) != VCHIQ_SUCCESS)) + return VCHIQ_ERROR; + + for (i = 0; i < (unsigned int)count; i++) +@@ -2361,7 +2399,7 @@ vchiq_release_message(VCHIQ_SERVICE_HAND + int slot_index; + int msgid; + +- if (service == NULL) ++ if (!is_valid_service(service)) + return; + + state = service->state; +@@ -2418,7 +2456,7 @@ vchiq_set_service_option(VCHIQ_SERVICE_H + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + VCHIQ_STATUS_T status = VCHIQ_ERROR; + +- if (service) ++ if (is_valid_service(service)) + { + switch (option) + { +@@ -2427,6 +2465,48 @@ vchiq_set_service_option(VCHIQ_SERVICE_H + status = VCHIQ_SUCCESS; + break; + ++ case VCHIQ_SERVICE_OPTION_SLOT_QUOTA: ++ { ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &service->state->service_quotas[service->localport]; ++ if (value == 0) ++ value = service->state->default_slot_quota; ++ if ((value >= service_quota->slot_use_count) && ++ (value < (unsigned short)~0)) ++ { ++ service_quota->slot_quota = value; ++ if ((value >= service_quota->slot_use_count) && ++ (service_quota->message_quota >= service_quota->message_use_count)) ++ { ++ /* Signal the service that it may have dropped below its quota */ ++ vcos_event_signal(&service_quota->quota_event); ++ } ++ status = VCHIQ_SUCCESS; ++ } ++ } ++ break; ++ ++ case VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA: ++ { ++ VCHIQ_SERVICE_QUOTA_T *service_quota = ++ &service->state->service_quotas[service->localport]; ++ if (value == 0) ++ value = service->state->default_message_quota; ++ if ((value >= service_quota->message_use_count) && ++ (value < (unsigned short)~0)) ++ { ++ service_quota->message_quota = value; ++ if ((value >= service_quota->message_use_count) && ++ (service_quota->slot_quota >= service_quota->slot_use_count)) ++ { ++ /* Signal the service that it may have dropped below its quota */ ++ vcos_event_signal(&service_quota->quota_event); ++ } ++ status = VCHIQ_SUCCESS; ++ } ++ } ++ break; ++ + default: + break; + } +@@ -2568,9 +2648,11 @@ vchiq_dump_service_state(void *dump_cont + vcos_strcpy(remoteport, "n/a"); + + len += vcos_snprintf(buf + len, sizeof(buf) - len, +- " '%c%c%c%c' remote %s (slot use %d/%d)", ++ " '%c%c%c%c' remote %s (msg use %d/%d, slot use %d/%d)", + VCHIQ_FOURCC_AS_4CHARS(fourcc), + remoteport, ++ service_quota->message_use_count, ++ service_quota->message_quota, + service_quota->slot_use_count, + service_quota->slot_quota); + +@@ -2602,3 +2684,34 @@ vchiq_dump_service_state(void *dump_cont + + vchiq_dump_platform_service_state(dump_context, service); + } ++ ++ ++VCHIQ_STATUS_T vchiq_send_remote_use(VCHIQ_STATE_T * state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_RETRY; ++ if(state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) ++ { ++ status = queue_message(state, NULL, VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE, 0, 0), NULL, 0, 0, 0); ++ } ++ return status; ++} ++ ++VCHIQ_STATUS_T vchiq_send_remote_release(VCHIQ_STATE_T * state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_RETRY; ++ if(state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) ++ { ++ status = queue_message(state, NULL, VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_RELEASE, 0, 0), NULL, 0, 0, 0); ++ } ++ return status; ++} ++ ++VCHIQ_STATUS_T vchiq_send_remote_use_active(VCHIQ_STATE_T * state) ++{ ++ VCHIQ_STATUS_T status = VCHIQ_RETRY; ++ if(state->conn_state != VCHIQ_CONNSTATE_DISCONNECTED) ++ { ++ status = queue_message(state, NULL, VCHIQ_MAKE_MSG(VCHIQ_MSG_REMOTE_USE_ACTIVE, 0, 0), NULL, 0, 0, 0); ++ } ++ return status; ++} +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_core.h +@@ -47,6 +47,9 @@ vcos_static_assert(IS_POW2(VCHIQ_MAX_SLO + #define VCHIQ_MSG_BULK_TX_DONE 9 // + (srcport, dstport), actual + #define VCHIQ_MSG_PAUSE 10 // - + #define VCHIQ_MSG_RESUME 11 // - ++#define VCHIQ_MSG_REMOTE_USE 12 // - ++#define VCHIQ_MSG_REMOTE_RELEASE 13 // - ++#define VCHIQ_MSG_REMOTE_USE_ACTIVE 14 // - + + #define VCHIQ_PORT_MAX (VCHIQ_MAX_SERVICES - 1) + #define VCHIQ_PORT_FREE 0x1000 +@@ -194,6 +197,8 @@ typedef struct remote_event_struct { + VCOS_EVENT_T * event; + } REMOTE_EVENT_T; + ++typedef struct opaque_platform_state_t* VCHIQ_PLATFORM_STATE_T; ++ + typedef struct vchiq_state_struct VCHIQ_STATE_T; + + typedef struct vchiq_slot_struct { +@@ -253,8 +258,10 @@ typedef struct vchiq_service_struct { + usage is carried over between users of the same port number. + */ + typedef struct vchiq_service_quota_struct { +- int slot_quota; +- int slot_use_count; ++ unsigned short slot_quota; ++ unsigned short slot_use_count; ++ unsigned short message_quota; ++ unsigned short message_use_count; + VCOS_EVENT_T quota_event; + int previous_tx_index; + } VCHIQ_SERVICE_QUOTA_T; +@@ -314,7 +321,8 @@ struct vchiq_state_struct { + VCHIQ_SHARED_STATE_T *remote; + VCHIQ_SLOT_T *slot_data; + +- int default_slot_quota; ++ unsigned short default_slot_quota; ++ unsigned short default_message_quota; + + VCOS_EVENT_T connect; // event indicating connect message received + VCOS_MUTEX_T mutex; // mutex protecting services +@@ -322,7 +330,6 @@ struct vchiq_state_struct { + + VCOS_THREAD_T slot_handler_thread; // processes incoming messages + VCOS_THREAD_T recycle_thread; // processes recycled slots +- VCOS_THREAD_T lp_thread; // processes low priority messages (eg suspend) + + /* Local implementation of the trigger remote event */ + VCOS_EVENT_T trigger_event; +@@ -330,8 +337,6 @@ struct vchiq_state_struct { + /* Local implementation of the recycle remote event */ + VCOS_EVENT_T recycle_event; + +- VCOS_EVENT_T lp_evt; +- + char *tx_data; + char *rx_data; + VCHIQ_SLOT_INFO_T *rx_info; +@@ -340,17 +345,6 @@ struct vchiq_state_struct { + + VCOS_MUTEX_T recycle_mutex; + +- VCOS_MUTEX_T suspend_resume_mutex; +- VCOS_MUTEX_T use_count_mutex; +- +- /* Global use count for videocore. +- * This is equal to the sum of the use counts for all services. When this hits +- * zero the videocore suspend procedure will be initiated. */ +- int videocore_use_count; +- +- /* Flag to indicate whether videocore is currently suspended */ +- int videocore_suspended; +- + /* Indicates the byte position within the stream from where the next message + will be read. The least significant bits are an index into the slot. + The next bits are the index of the slot in remote->slot_queue. */ +@@ -388,6 +382,8 @@ struct vchiq_state_struct { + VCHIQ_SERVICE_T *services[VCHIQ_MAX_SERVICES]; + VCHIQ_SERVICE_QUOTA_T service_quotas[VCHIQ_MAX_SERVICES]; + VCHIQ_SLOT_INFO_T slot_info[VCHIQ_MAX_SLOTS]; ++ ++ VCHIQ_PLATFORM_STATE_T platform_state; + }; + + extern VCHIQ_SLOT_ZERO_T * +@@ -477,4 +473,34 @@ extern void + vchiq_dump_platform_service_state(void *dump_context, + VCHIQ_SERVICE_T *service); + ++extern VCHIQ_STATUS_T ++vchiq_use_service_internal(VCHIQ_SERVICE_T *service); ++ ++extern VCHIQ_STATUS_T ++vchiq_release_service_internal(VCHIQ_SERVICE_T *service); ++ ++extern VCHIQ_STATUS_T ++vchiq_on_remote_use(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_on_remote_release(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_platform_init_state(VCHIQ_STATE_T *state); ++ ++extern void ++vchiq_on_remote_use_active(VCHIQ_STATE_T *state); ++ ++extern VCHIQ_STATUS_T ++vchiq_send_remote_use(VCHIQ_STATE_T * state); ++ ++extern VCHIQ_STATUS_T ++vchiq_send_remote_release(VCHIQ_STATE_T * state); ++ ++extern VCHIQ_STATUS_T ++vchiq_send_remote_use_active(VCHIQ_STATE_T * state); ++ ++extern void ++vchiq_platform_conn_state_changed(VCHIQ_STATE_T* state, VCHIQ_CONNSTATE_T oldstate, VCHIQ_CONNSTATE_T newstate); ++ + #endif +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_if.h +@@ -55,7 +55,9 @@ typedef enum + + typedef enum + { +- VCHIQ_SERVICE_OPTION_AUTOCLOSE ++ VCHIQ_SERVICE_OPTION_AUTOCLOSE, ++ VCHIQ_SERVICE_OPTION_SLOT_QUOTA, ++ VCHIQ_SERVICE_OPTION_MESSAGE_QUOTA + } VCHIQ_SERVICE_OPTION_T; + + #ifdef __HIGHC__ +@@ -94,11 +96,11 @@ typedef struct vchiq_service_base_struct + } VCHIQ_SERVICE_BASE_T; + + typedef struct vchiq_service_params_struct { +- int fourcc; +- VCHIQ_CALLBACK_T callback; +- void *userdata; +- short version; /* Increment for non-trivial changes */ +- short version_min; /* Update for incompatible changes */ ++ int fourcc; ++ VCHIQ_CALLBACK_T callback; ++ void *userdata; ++ short version; /* Increment for non-trivial changes */ ++ short version_min; /* Update for incompatible changes */ + } VCHIQ_SERVICE_PARAMS_T; + + typedef struct vchiq_config_struct { +@@ -112,6 +114,8 @@ typedef struct vchiq_config_struct { + } VCHIQ_CONFIG_T; + + typedef struct vchiq_instance_struct *VCHIQ_INSTANCE_T; ++typedef void (*VCHIQ_REMOTE_USE_CALLBACK_T)(void* cb_arg); ++ + + extern VCHIQ_STATUS_T vchiq_initialise(VCHIQ_INSTANCE_T *pinstance); + extern VCHIQ_STATUS_T vchiq_shutdown(VCHIQ_INSTANCE_T instance); +@@ -143,6 +147,9 @@ extern int vchiq_get_client_i + extern VCHIQ_STATUS_T vchiq_get_config(VCHIQ_INSTANCE_T instance, int config_size, VCHIQ_CONFIG_T *pconfig); + extern VCHIQ_STATUS_T vchiq_set_service_option(VCHIQ_SERVICE_HANDLE_T service, VCHIQ_SERVICE_OPTION_T option, int value); + ++extern VCHIQ_STATUS_T vchiq_remote_use(VCHIQ_INSTANCE_T instance, VCHIQ_REMOTE_USE_CALLBACK_T callback, void* cb_arg); ++extern VCHIQ_STATUS_T vchiq_remote_release(VCHIQ_INSTANCE_T instance); ++ + extern VCHIQ_STATUS_T vchiq_dump_phys_mem( VCHIQ_SERVICE_HANDLE_T service, void *ptr, size_t num_bytes ); + + #endif /* VCHIQ_IF_H */ +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_ioctl.h +@@ -91,15 +91,15 @@ typedef struct { + #define VCHIQ_IOC_QUEUE_MESSAGE _IOW(VCHIQ_IOC_MAGIC, 4, VCHIQ_QUEUE_MESSAGE_T) + #define VCHIQ_IOC_QUEUE_BULK_TRANSMIT _IOW(VCHIQ_IOC_MAGIC, 5, VCHIQ_QUEUE_BULK_TRANSFER_T) + #define VCHIQ_IOC_QUEUE_BULK_RECEIVE _IOW(VCHIQ_IOC_MAGIC, 6, VCHIQ_QUEUE_BULK_TRANSFER_T) +-#define VCHIQ_IOC_AWAIT_COMPLETION _IOW(VCHIQ_IOC_MAGIC, 7, VCHIQ_AWAIT_COMPLETION_T) +-#define VCHIQ_IOC_DEQUEUE_MESSAGE _IOW(VCHIQ_IOC_MAGIC, 8, VCHIQ_DEQUEUE_MESSAGE_T) ++#define VCHIQ_IOC_AWAIT_COMPLETION _IOWR(VCHIQ_IOC_MAGIC, 7, VCHIQ_AWAIT_COMPLETION_T) ++#define VCHIQ_IOC_DEQUEUE_MESSAGE _IOWR(VCHIQ_IOC_MAGIC, 8, VCHIQ_DEQUEUE_MESSAGE_T) + #define VCHIQ_IOC_GET_CLIENT_ID _IO(VCHIQ_IOC_MAGIC, 9) +-#define VCHIQ_IOC_GET_CONFIG _IOW(VCHIQ_IOC_MAGIC, 10, VCHIQ_GET_CONFIG_T) +-#define VCHIQ_IOC_CLOSE_SERVICE _IO(VCHIQ_IOC_MAGIC, 11) +-#define VCHIQ_IOC_USE_SERVICE _IO(VCHIQ_IOC_MAGIC, 12) +-#define VCHIQ_IOC_RELEASE_SERVICE _IO(VCHIQ_IOC_MAGIC, 13) +-#define VCHIQ_IOC_SET_SERVICE_OPTION _IOW(VCHIQ_IOC_MAGIC, 14, VCHIQ_SET_SERVICE_OPTION_T) +-#define VCHIQ_IOC_DUMP_PHYS_MEM _IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM_T) ++#define VCHIQ_IOC_GET_CONFIG _IOWR(VCHIQ_IOC_MAGIC, 10, VCHIQ_GET_CONFIG_T) ++#define VCHIQ_IOC_CLOSE_SERVICE _IO(VCHIQ_IOC_MAGIC, 11) ++#define VCHIQ_IOC_USE_SERVICE _IO(VCHIQ_IOC_MAGIC, 12) ++#define VCHIQ_IOC_RELEASE_SERVICE _IO(VCHIQ_IOC_MAGIC, 13) ++#define VCHIQ_IOC_SET_SERVICE_OPTION _IOW(VCHIQ_IOC_MAGIC, 14, VCHIQ_SET_SERVICE_OPTION_T) ++#define VCHIQ_IOC_DUMP_PHYS_MEM _IOW(VCHIQ_IOC_MAGIC, 15, VCHIQ_DUMP_MEM_T) + #define VCHIQ_IOC_MAX 15 + + #endif +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_lib.c ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_lib.c +@@ -97,6 +97,12 @@ is_valid_instance(VCHIQ_INSTANCE_T insta + return (instance == &vchiq_instance) && (instance->initialised > 0); + } + ++static __inline int ++is_valid_service(VCHIQ_SERVICE_T *service) ++{ ++ return ((service != NULL) && (service->fd != VCHIQ_INVALID_HANDLE)); ++} ++ + /* + * VCHIQ API + */ +@@ -318,6 +324,9 @@ vchiq_close_service(VCHIQ_SERVICE_HANDLE + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_CLOSE_SERVICE, service->handle)); + + if (ret != 0) +@@ -335,6 +344,9 @@ vchiq_remove_service(VCHIQ_SERVICE_HANDL + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle)); + + if (ret != 0) +@@ -355,6 +367,9 @@ vchiq_queue_message(VCHIQ_SERVICE_HANDLE + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + args.handle = service->handle; + args.elements = elements; + args.count = count; +@@ -384,6 +399,9 @@ vchiq_queue_bulk_transmit(VCHIQ_SERVICE_ + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + args.handle = service->handle; + args.data = (void *)data; + args.size = size; +@@ -406,6 +424,9 @@ vchiq_queue_bulk_receive(VCHIQ_SERVICE_H + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + args.handle = service->handle; + args.data = data; + args.size = size; +@@ -457,6 +478,9 @@ vchiq_bulk_transmit(VCHIQ_SERVICE_HANDLE + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + args.handle = service->handle; + args.data = (void *)data; + args.size = size; +@@ -480,6 +504,9 @@ vchiq_bulk_receive(VCHIQ_SERVICE_HANDLE_ + + vcos_log_trace( "%s called service handle = 0x%08x", __func__, (uint32_t)handle ); + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + args.handle = service->handle; + args.data = data; + args.size = size; +@@ -521,6 +548,9 @@ vchiq_get_client_id(VCHIQ_SERVICE_HANDLE + { + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + return ioctl(service->fd, VCHIQ_IOC_GET_CLIENT_ID, service->handle); + } + +@@ -546,10 +576,14 @@ vchiq_get_config(VCHIQ_INSTANCE_T instan + int32_t + vchiq_use_service( const VCHIQ_SERVICE_HANDLE_T handle ) + { +- VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; +- int ret; +- RETRY(ret,ioctl(service->fd, VCHIQ_IOC_USE_SERVICE, service->handle)); +- return ret; ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ int ret; ++ ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ ++ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_USE_SERVICE, service->handle)); ++ return ret; + } + + int32_t +@@ -569,6 +603,9 @@ vchiq_set_service_option(VCHIQ_SERVICE_H + VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; + int ret; + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + args.handle = service->handle; + args.option = option; + args.value = value; +@@ -633,6 +670,9 @@ vchi_msg_peek( VCHI_SERVICE_HANDLE_T han + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + int ret; + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + ret = fill_peek_buf(service, flags); + + if (ret == 0) +@@ -659,6 +699,9 @@ vchi_msg_remove( VCHI_SERVICE_HANDLE_T h + { + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + /* Why would you call vchi_msg_remove without calling vchi_msg_peek first? */ + vcos_assert(service->peek_size >= 0); + +@@ -697,6 +740,9 @@ vchi_msg_queue( VCHI_SERVICE_HANDLE_T ha + vcos_unused(msg_handle); + vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED); + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + args.handle = service->handle; + args.elements = &element; + args.count = 1; +@@ -730,6 +776,9 @@ vchi_bulk_queue_receive( VCHI_SERVICE_HA + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + switch ((int)flags) { + case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: + args.mode = VCHIQ_BULK_MODE_CALLBACK; +@@ -780,6 +829,9 @@ vchi_bulk_queue_transmit( VCHI_SERVICE_H + VCHIQ_QUEUE_BULK_TRANSFER_T args; + int ret; + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + switch ((int)flags) { + case VCHI_FLAGS_CALLBACK_WHEN_OP_COMPLETE | VCHI_FLAGS_BLOCK_UNTIL_QUEUED: + args.mode = VCHIQ_BULK_MODE_CALLBACK; +@@ -833,6 +885,9 @@ vchi_msg_dequeue( VCHI_SERVICE_HANDLE_T + + vcos_assert(flags == VCHI_FLAGS_NONE || flags == VCHI_FLAGS_BLOCK_UNTIL_OP_COMPLETE); + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + if (service->peek_size >= 0) + { + fprintf(stderr, "vchi_msg_dequeue -> using peek buffer\n"); +@@ -903,6 +958,9 @@ vchi_msg_queuev( VCHI_SERVICE_HANDLE_T h + + vcos_assert(flags == VCHI_FLAGS_BLOCK_UNTIL_QUEUED); + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + args.handle = service->handle; + args.elements = (const VCHIQ_ELEMENT_T *)vector; + args.count = count; +@@ -961,6 +1019,9 @@ vchi_msg_hold( VCHI_SERVICE_HANDLE_T han + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + int ret; + ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + ret = fill_peek_buf(service, flags); + + if (ret == 0) +@@ -1116,6 +1177,10 @@ vchi_service_close( const VCHI_SERVICE_H + { + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + int ret; ++ ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle)); + + if (ret == 0) +@@ -1129,6 +1194,10 @@ vchi_service_destroy( const VCHI_SERVICE + { + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + int ret; ++ ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_REMOVE_SERVICE, service->handle)); + + if (ret == 0) +@@ -1200,6 +1269,10 @@ vchi_service_use( const VCHI_SERVICE_HAN + { + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + int ret; ++ ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_USE_SERVICE, service->handle)); + return ret; + } +@@ -1218,10 +1291,47 @@ int32_t vchi_service_release( const VCHI + { + VCHI_SERVICE_T *service = (VCHI_SERVICE_T *)handle; + int ret; ++ ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ + RETRY(ret,ioctl(service->fd, VCHIQ_IOC_RELEASE_SERVICE, service->handle)); + return ret; + } + ++/*********************************************************** ++ * Name: vchiq_dump_phys_mem ++ * ++ * Arguments: const VCHI_SERVICE_HANDLE_T handle ++ * void *buffer ++ * size_t num_bytes ++ * ++ * Description: Dumps the physical memory associated with ++ * a buffer. ++ * ++ * Returns: void ++ * ++ ***********************************************************/ ++VCHIQ_STATUS_T vchiq_dump_phys_mem( VCHIQ_SERVICE_HANDLE_T handle, ++ void *ptr, ++ size_t num_bytes ) ++{ ++ VCHIQ_SERVICE_T *service = (VCHIQ_SERVICE_T *)handle; ++ VCHIQ_DUMP_MEM_T dump_mem; ++ int ret; ++ ++ if (!is_valid_service(service)) ++ return VCHIQ_ERROR; ++ ++ dump_mem.virt_addr = ptr; ++ dump_mem.num_bytes = num_bytes; ++ ++ RETRY(ret,ioctl(service->fd, VCHIQ_IOC_DUMP_PHYS_MEM, &dump_mem)); ++ return (ret >= 0) ? VCHIQ_SUCCESS : VCHIQ_ERROR; ++} ++ ++ ++ + /* + * Support functions + */ +--- a/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c ++++ b/drivers/misc/vc04_services/interface/vchiq_arm/vchiq_shim.c +@@ -859,10 +859,38 @@ int32_t vchi_service_create( VCHI_INSTAN + + int32_t vchi_service_close( const VCHI_SERVICE_HANDLE_T handle ) + { +- vcos_unused(handle); ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if(service) ++ { ++ VCHIQ_STATUS_T status = vchiq_close_service(service->handle); ++ if (status == VCHIQ_SUCCESS) ++ { ++ service_free(service); ++ service = NULL; ++ } ++ ++ ret = vchiq_status_to_vchi( status ); ++ } ++ return ret; ++} + +- // YTI?? +- return 0; ++int32_t vchi_service_destroy( const VCHI_SERVICE_HANDLE_T handle ) ++{ ++ int32_t ret = -1; ++ SHIM_SERVICE_T *service = (SHIM_SERVICE_T *)handle; ++ if(service) ++ { ++ VCHIQ_STATUS_T status = vchiq_remove_service(service->handle); ++ if (status == VCHIQ_SUCCESS) ++ { ++ service_free(service); ++ service = NULL; ++ } ++ ++ ret = vchiq_status_to_vchi( status ); ++ } ++ return ret; + } + + /* ---------------------------------------------------------------------- +@@ -962,9 +990,12 @@ EXPORT_SYMBOL(vchi_bulk_queue_transmit); + EXPORT_SYMBOL(vchi_msg_dequeue); + EXPORT_SYMBOL(vchi_msg_queue); + EXPORT_SYMBOL(vchi_msg_queuev); ++EXPORT_SYMBOL(vchi_msg_peek); ++EXPORT_SYMBOL(vchi_msg_remove); + EXPORT_SYMBOL(vchi_service_close); + EXPORT_SYMBOL(vchi_service_open); + EXPORT_SYMBOL(vchi_service_create); ++EXPORT_SYMBOL(vchi_service_destroy); + EXPORT_SYMBOL(vchi_service_use); + EXPORT_SYMBOL(vchi_service_release); + #endif +--- a/drivers/mmc/host/sdhci-bcm2708.c ++++ b/drivers/mmc/host/sdhci-bcm2708.c +@@ -26,7 +26,9 @@ + #include <linux/highmem.h> + #include <linux/platform_device.h> + #include <linux/module.h> ++#include <linux/mmc/mmc.h> + #include <linux/mmc/host.h> ++#include <linux/mmc/sd.h> + + #include <linux/io.h> + #include <linux/dma-mapping.h> +@@ -57,6 +59,9 @@ + //#define LOG_REGISTERS + + #define USE_SCHED_TIME ++#define USE_SPACED_WRITES_2CLK 1 /* space consecutive register writes */ ++#define USE_SOFTWARE_TIMEOUTS 1 /* not hardware timeouts */ ++#define SOFTWARE_ERASE_TIMEOUT_SEC 30 + + #define SDHCI_BCM_DMA_CHAN 4 /* this default is normally overriden */ + #define SDHCI_BCM_DMA_WAITS 0 /* delays slowing DMA transfers: 0-31 */ +@@ -68,6 +73,9 @@ + + #define BCM2708_SDHCI_SLEEP_TIMEOUT 1000 /* msecs */ + ++/* Mhz clock that the EMMC core is running at. Should match the platform clockman settings */ ++#define BCM2708_EMMC_CLOCK_FREQ 80000000 ++ + #define POWER_OFF 0 + #define POWER_LAZY_OFF 1 + #define POWER_ON 2 +@@ -222,6 +230,12 @@ u8 sdhci_bcm2708_readb(struct sdhci_host + + static void sdhci_bcm2708_raw_writel(struct sdhci_host *host, u32 val, int reg) + { ++ u32 ier; ++ ++#if USE_SPACED_WRITES_2CLK ++ static bool timeout_disabled = false; ++ unsigned int ns_2clk = 0; ++ + /* The Arasan has a bugette whereby it may lose the content of + * successive writes to registers that are within two SD-card clock + * cycles of each other (a clock domain crossing problem). +@@ -229,12 +243,11 @@ static void sdhci_bcm2708_raw_writel(str + * (Which is just as well - otherwise we'd have to nobble the DMA engine + * too) + */ +-#if 1 + if (reg != SDHCI_BUFFER && host->clock != 0) { + /* host->clock is the clock freq in Hz */ + static hptime_t last_write_hpt; + hptime_t now = hptime(); +- unsigned int ns_2clk = 2000000000/host->clock; ++ ns_2clk = 2000000000/host->clock; + + if (now == last_write_hpt || now == last_write_hpt+1) { + /* we can't guarantee any significant time has +@@ -250,6 +263,27 @@ static void sdhci_bcm2708_raw_writel(str + } + last_write_hpt = now; + } ++#if USE_SOFTWARE_TIMEOUTS ++ /* The Arasan is clocked for timeouts using the SD clock which is too ++ * fast for ERASE commands and causes issues. So we disable timeouts ++ * for ERASE */ ++ if (host->cmd != NULL && host->cmd->opcode == MMC_ERASE && ++ reg == (SDHCI_COMMAND & ~3)) { ++ mod_timer(&host->timer, ++ jiffies + SOFTWARE_ERASE_TIMEOUT_SEC * HZ); ++ ier = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE); ++ ier &= ~SDHCI_INT_DATA_TIMEOUT; ++ writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE); ++ timeout_disabled = true; ++ udelay((ns_2clk+1000-1)/1000); ++ } else if (timeout_disabled) { ++ ier = readl(host->ioaddr + SDHCI_SIGNAL_ENABLE); ++ ier |= SDHCI_INT_DATA_TIMEOUT; ++ writel(ier, host->ioaddr + SDHCI_SIGNAL_ENABLE); ++ timeout_disabled = false; ++ udelay((ns_2clk+1000-1)/1000); ++ } ++#endif + writel(val, host->ioaddr + reg); + #else + void __iomem * regaddr = host->ioaddr + reg; +@@ -325,14 +359,68 @@ void sdhci_bcm2708_writeb(struct sdhci_h + + static unsigned int sdhci_bcm2708_get_max_clock(struct sdhci_host *host) + { +- return 100000000; // this value is in Hz (100MHz/4) ++ return 20000000; // this value is in Hz (20MHz) + } + + static unsigned int sdhci_bcm2708_get_timeout_clock(struct sdhci_host *host) + { +- return 100000; // this value is in kHz (100MHz/4) ++ if(host->clock) ++ return (host->clock / 1000); // this value is in kHz (100MHz) ++ else ++ return (sdhci_bcm2708_get_max_clock(host) / 1000); + } + ++static void sdhci_bcm2708_set_clock(struct sdhci_host *host, unsigned int clock) ++{ ++ int div = 0; ++ u16 clk = 0; ++ unsigned long timeout; ++ ++ if (clock == host->clock) ++ return; ++ ++ sdhci_writew(host, 0, SDHCI_CLOCK_CONTROL); ++ ++ if (clock == 0) ++ goto out; ++ ++ if (BCM2708_EMMC_CLOCK_FREQ <= clock) ++ div = 1; ++ else { ++ for (div = 2; div < SDHCI_MAX_DIV_SPEC_300; div += 2) { ++ if ((BCM2708_EMMC_CLOCK_FREQ / div) <= clock) ++ break; ++ } ++ } ++ ++ DBG( "desired SD clock: %d, actual: %d\n", ++ clock, BCM2708_EMMC_CLOCK_FREQ / div); ++ ++ clk |= (div & SDHCI_DIV_MASK) << SDHCI_DIVIDER_SHIFT; ++ clk |= ((div & SDHCI_DIV_HI_MASK) >> SDHCI_DIV_MASK_LEN) ++ << SDHCI_DIVIDER_HI_SHIFT; ++ clk |= SDHCI_CLOCK_INT_EN; ++ ++ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); ++ ++ timeout = 20; ++ while (!((clk = sdhci_readw(host, SDHCI_CLOCK_CONTROL)) ++ & SDHCI_CLOCK_INT_STABLE)) { ++ if (timeout == 0) { ++ printk(KERN_ERR "%s: Internal clock never " ++ "stabilised.\n", mmc_hostname(host->mmc)); ++ return; ++ } ++ timeout--; ++ mdelay(1); ++ } ++ ++ clk |= SDHCI_CLOCK_CARD_EN; ++ sdhci_writew(host, clk, SDHCI_CLOCK_CONTROL); ++out: ++ host->clock = clock; ++ } ++ + /*****************************************************************************\ + * * + * DMA Operation * +@@ -429,7 +517,8 @@ static void schci_bcm2708_cb_read(struct + cb->stride = 0; + + if (is_last) { +- cb->info |= BCM2708_DMA_INT_EN; ++ cb->info |= BCM2708_DMA_INT_EN | ++ BCM2708_DMA_WAIT_RESP; + cb->next = 0; + } else + cb->next = host->cb_handle + +@@ -460,7 +549,8 @@ static void schci_bcm2708_cb_write(struc + cb->stride = 0; + + if (is_last) { +- cb->info |= BCM2708_DMA_INT_EN; ++ cb->info |= BCM2708_DMA_INT_EN | ++ BCM2708_DMA_WAIT_RESP; + cb->next = 0; + } else + cb->next = host->cb_handle + +@@ -806,8 +896,7 @@ static void sdhci_bcm2708_dma_complete_i + We get CRC and DEND errors unless we wait for + the SD controller to finish reading/writing to the card. */ + u32 state_mask; +- int timeout=1000000; +- hptime_t now = hptime(); ++ int timeout=1000; + + DBG("PDMA over - sync card\n"); + if (data->flags & MMC_DATA_READ) +@@ -815,17 +904,12 @@ static void sdhci_bcm2708_dma_complete_i + else + state_mask = SDHCI_DOING_WRITE; + +- while (0 != (sdhci_bcm2708_raw_readl(host, +- SDHCI_PRESENT_STATE) & +- state_mask) && --timeout > 0) ++ while (0 != (sdhci_bcm2708_raw_readl(host, SDHCI_PRESENT_STATE) ++ & state_mask) && --timeout > 0) ++ { ++ udelay(100); + continue; +- +- if (1000000-timeout > 4000) /*ave. is about 3250*/ +- printk(KERN_INFO "%s: note - long %s sync %luns - " +- "%d its.\n", +- mmc_hostname(host->mmc), +- data->flags & MMC_DATA_READ? "read": "write", +- since_ns(now), 1000000-timeout); ++ } + if (timeout <= 0) + printk(KERN_ERR"%s: final %s to SD card still " + "running\n", +@@ -1201,6 +1285,11 @@ static unsigned int sdhci_bcm2708_uhs_br + return 1; + } + ++static unsigned int sdhci_bcm2708_missing_status(struct sdhci_host *host) ++{ ++ return 1; ++} ++ + /***************************************************************************** \ + * * + * Device ops * +@@ -1219,7 +1308,7 @@ static struct sdhci_ops sdhci_bcm2708_op + #error The BCM2708 SDHCI driver needs CONFIG_MMC_SDHCI_IO_ACCESSORS to be set + #endif + //.enable_dma = NULL, +- //.set_clock = NULL, ++ .set_clock = sdhci_bcm2708_set_clock, + .get_max_clock = sdhci_bcm2708_get_max_clock, + //.get_min_clock = NULL, + .get_timeout_clock = sdhci_bcm2708_get_timeout_clock, +@@ -1238,6 +1327,7 @@ static struct sdhci_ops sdhci_bcm2708_op + .spurious_crc_acmd51 = sdhci_bcm2708_quirk_spurious_crc, + .voltage_broken = sdhci_bcm2708_quirk_voltage_broken, + .uhs_broken = sdhci_bcm2708_uhs_broken, ++ .missing_status = sdhci_bcm2708_missing_status, + }; + + /*****************************************************************************\ +@@ -1282,7 +1372,9 @@ static int __devinit sdhci_bcm2708_probe + host->irq = platform_get_irq(pdev, 0); + + host->quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION | +- SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK; ++ SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK | ++ SDHCI_QUIRK_BROKEN_TIMEOUT_VAL | ++ SDHCI_QUIRK_NONSTANDARD_CLOCK; + #ifdef CONFIG_MMC_SDHCI_BCM2708_DMA + host->flags = SDHCI_USE_PLATDMA; + #endif +@@ -1349,6 +1441,8 @@ static int __devinit sdhci_bcm2708_probe + host_priv->cb_base, (unsigned)host_priv->cb_handle, + host_priv->dma_chan, host_priv->dma_chan_base, + host_priv->dma_irq); ++ ++ host->mmc->caps |= MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED; + #endif + + ret = sdhci_add_host(host); +--- a/drivers/mmc/host/sdhci.c ++++ b/drivers/mmc/host/sdhci.c +@@ -974,6 +974,12 @@ static void sdhci_send_command(struct sd + if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY)) + mask |= SDHCI_DATA_INHIBIT; + ++ if(host->ops->missing_status && (cmd->opcode == MMC_SEND_STATUS)) { ++ timeout = 5000; // Really obscenely large delay to send the status, due to bug in controller ++ // which might cause the STATUS command to get stuck when a data operation is in flow ++ mask |= SDHCI_DATA_INHIBIT; ++ } ++ + /* We shouldn't wait for data inihibit for stop commands, even + though they might use busy signaling */ + if (host->mrq->data && (cmd == host->mrq->data->stop)) +@@ -2098,7 +2104,7 @@ static void sdhci_timeout_timer(unsigned + + if (host->mrq) { + pr_err("%s: Timeout waiting for hardware " +- "interrupt.\n", mmc_hostname(host->mmc)); ++ "interrupt - cmd%d.\n", mmc_hostname(host->mmc), host->last_cmdop); + sdhci_dumpregs(host); + + if (host->data) { +@@ -3065,8 +3071,11 @@ int sdhci_add_host(struct sdhci_host *ho + mmc->caps |= MMC_CAP_MAX_CURRENT_200; + } + +- if(host->ops->voltage_broken) +- ocr_avail |= MMC_VDD_29_30 | MMC_VDD_30_31; ++ if(host->ops->voltage_broken) { ++ ocr_avail |= MMC_VDD_32_33 | MMC_VDD_33_34; ++ // Cannot support UHS modes is we are stuck at 3.3V; ++ mmc->caps &= ~(MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_DDR50); ++ } + + mmc->ocr_avail = ocr_avail; + mmc->ocr_avail_sdio = ocr_avail; +--- a/drivers/mmc/host/sdhci.h ++++ b/drivers/mmc/host/sdhci.h +@@ -291,6 +291,7 @@ struct sdhci_ops { + unsigned int (*spurious_crc_acmd51)(struct sdhci_host *host); + unsigned int (*voltage_broken)(struct sdhci_host *host); + unsigned int (*uhs_broken)(struct sdhci_host *host); ++ unsigned int (*missing_status)(struct sdhci_host *host); + + void (*hw_reset)(struct sdhci_host *host); + }; +--- a/drivers/net/usb/smsc95xx.c ++++ b/drivers/net/usb/smsc95xx.c +@@ -1073,6 +1073,7 @@ static int smsc95xx_bind(struct usbnet * + dev->net->ethtool_ops = &smsc95xx_ethtool_ops; + dev->net->flags |= IFF_MULTICAST; + dev->net->hard_header_len += SMSC95XX_TX_OVERHEAD_CSUM; ++ dev->hard_mtu = dev->net->mtu + dev->net->hard_header_len; + return 0; + } + +--- a/drivers/usb/gadget/Kconfig ++++ b/drivers/usb/gadget/Kconfig +@@ -536,28 +536,6 @@ config USB_GADGET_SUPERSPEED + bool + depends on USB_GADGET_DUALSPEED + +-config USB_GADGET_SNPS_DWC_OTG +- boolean "Synopsys Driver for DWC_otg Controller" +- depends on USB && EXPERIMENTAL +- select USB_OTG +- select USB_GADGET_DUALSPEED +- help +- Selects the Synopsys Driver for the DWC_otg Controller. +- +-config USB_DWC_OTG_LPM +- boolean "Enable LPM support" +- depends on USB && EXPERIMENTAL +- help +- Enables LPM support. +- +-config USB_GADGET_SNPS_DWC_OTG +- boolean "Synopsys Driver for DWC_otg Controller" +- depends on USB && EXPERIMENTAL +- select USB_OTG +- select USB_GADGET_DUALSPEED +- help +- Selects the Synopsys Driver for the DWC_otg Controller. +- + config USB_DWC_OTG_LPM + boolean "Enable LPM support" + depends on USB && EXPERIMENTAL +--- a/drivers/usb/host/dwc_common_port/Makefile ++++ b/drivers/usb/host/dwc_common_port/Makefile +@@ -6,7 +6,9 @@ ifneq ($(KERNELRELEASE),) + + #CPPFLAGS += -DDEBUG_MEMORY + ++ifeq ($(CONFIG_USB_DEBUG),y) + CPPFLAGS += -DDEBUG ++endif + CPPFLAGS += -DDWC_LINUX + + obj-$(CONFIG_USB_DWCOTG) += dwc_common_port_lib.o +--- a/drivers/usb/host/dwc_common_port/dwc_os.h ++++ b/drivers/usb/host/dwc_common_port/dwc_os.h +@@ -216,6 +216,7 @@ extern void __DWC_DEBUG(char *format, .. + #endif + #else + #define __DWC_DEBUG printk ++#include <linux/kernel.h> + #endif + + /** +--- a/drivers/usb/host/dwc_otg/Makefile ++++ b/drivers/usb/host/dwc_otg/Makefile +@@ -9,7 +9,9 @@ ifeq ($(BUS_INTERFACE),) + BUS_INTERFACE = -DPLATFORM_INTERFACE=1 + endif + +-CPPFLAGS += -DDEBUG ++ifeq ($(CONFIG_USB_DEBUG),y) ++CPPFLAGS += -DDEBUG ++endif + + # Use one of the following flags to compile the software in host-only or + # device-only mode. +--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.c ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.c +@@ -909,6 +909,10 @@ static void assign_and_init_hc(dwc_otg_h + return 0; + #endif + ++ if (((urb->actual_length < 0) || (urb->actual_length > urb->length)) && !dwc_otg_hcd_is_pipe_in(&urb->pipe_info)) ++ urb->actual_length = urb->length; ++ ++ + hc = DWC_CIRCLEQ_FIRST(&hcd->free_hc_list); + + /* Remove the host channel from the free list. */ +--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd.h ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd.h +@@ -628,7 +628,7 @@ static inline void dwc_otg_hcd_qh_remove + * @return Returns the memory allocate or NULL on error. */ + static inline dwc_otg_qh_t *dwc_otg_hcd_qh_alloc(void) + { +- return (dwc_otg_qh_t *) dwc_alloc(sizeof(dwc_otg_qh_t)); ++ return (dwc_otg_qh_t *) dwc_alloc_atomic(sizeof(dwc_otg_qh_t)); + } + + extern dwc_otg_qtd_t *dwc_otg_hcd_qtd_create(dwc_otg_hcd_urb_t * urb); +@@ -640,7 +640,7 @@ extern int dwc_otg_hcd_qtd_add(dwc_otg_q + * @return Returns the memory allocate or NULL on error. */ + static inline dwc_otg_qtd_t *dwc_otg_hcd_qtd_alloc(void) + { +- return (dwc_otg_qtd_t *) dwc_alloc(sizeof(dwc_otg_qtd_t)); ++ return (dwc_otg_qtd_t *) dwc_alloc_atomic(sizeof(dwc_otg_qtd_t)); + } + + /** Frees the memory for a QTD structure. QTD should already be removed from +--- a/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c ++++ b/drivers/usb/host/dwc_otg/dwc_otg_hcd_linux.c +@@ -417,6 +417,9 @@ int hcd_init( + + hcd->regs = otg_dev->base; + ++ /* Integrate TT in root hub */ ++ hcd->has_tt = 1; ++ + /* Initialize the DWC OTG HCD. */ + dwc_otg_hcd = dwc_otg_hcd_alloc_hcd(); + if (!dwc_otg_hcd) { +@@ -668,6 +671,9 @@ static int urb_enqueue(struct usb_hcd *h + urb->number_of_packets, + mem_flags == GFP_ATOMIC ? 1 : 0); + ++ if(dwc_otg_urb == NULL) ++ return -ENOMEM; ++ + urb->hcpriv = dwc_otg_urb; + + dwc_otg_hcd_urb_set_pipeinfo(dwc_otg_urb, usb_pipedevice(urb->pipe), +@@ -755,10 +761,12 @@ static int urb_dequeue(struct usb_hcd *h + dump_urb_info(urb, "urb_dequeue"); + } + #endif +- dwc_otg_hcd_urb_dequeue(dwc_otg_hcd, (dwc_otg_hcd_urb_t *)urb->hcpriv); ++ if(urb->hcpriv != NULL) { ++ dwc_otg_hcd_urb_dequeue(dwc_otg_hcd, (dwc_otg_hcd_urb_t *)urb->hcpriv); + +- dwc_free(urb->hcpriv); +- urb->hcpriv = NULL; ++ urb->hcpriv = NULL; ++ dwc_free(urb->hcpriv); ++ } + + /* Higher layer software sets URB status. */ + #if (LINUX_VERSION_CODE < KERNEL_VERSION(2,6,30)) +--- a/drivers/video/bcm2708_fb.c ++++ b/drivers/video/bcm2708_fb.c +@@ -7,14 +7,17 @@ + * License. See the file COPYING in the main directory of this archive + * for more details. + * +- * Broadcom simple framebuffer driver ++ * Broadcom simple framebuffer driver ++ * ++ * This file is derived from cirrusfb.c ++ * Copyright 1999-2001 Jeff Garzik <jgarzik@pobox.com> ++ * + */ + #include <linux/module.h> + #include <linux/kernel.h> + #include <linux/errno.h> + #include <linux/string.h> + #include <linux/slab.h> +-#include <linux/delay.h> + #include <linux/mm.h> + #include <linux/fb.h> + #include <linux/init.h> +@@ -22,6 +25,8 @@ + #include <linux/list.h> + #include <linux/platform_device.h> + #include <linux/clk.h> ++#include <linux/printk.h> ++#include <linux/console.h> + + #include <mach/platform.h> + #include <mach/vcio.h> +@@ -38,26 +43,24 @@ static const char *bcm2708_name = "BCM27 + /* this data structure describes each frame buffer device we find */ + + struct fbinfo_s { +- int xres, yres, xres_virtual, yres_virtual; +- int pitch, bpp; +- int xoffset, yoffset; +- int base; +- int screen_size; ++ u32 xres, yres, xres_virtual, yres_virtual; ++ u32 pitch, bpp; ++ u32 xoffset, yoffset; ++ u32 base; ++ u32 screen_size; + }; + + struct bcm2708_fb { +- struct fb_info fb; +- struct platform_device *dev; +- void __iomem *regs; +- volatile struct fbinfo_s *info; +- dma_addr_t dma; +- u32 cmap[16]; ++ struct fb_info fb; ++ struct platform_device *dev; ++ struct fbinfo_s *info; ++ dma_addr_t dma; ++ u32 cmap[16]; + }; + + #define to_bcm2708(info) container_of(info, struct bcm2708_fb, fb) + +-static int +-bcm2708_fb_set_bitfields(struct fb_var_screeninfo *var) ++static int bcm2708_fb_set_bitfields(struct fb_var_screeninfo *var) + { + int ret = 0; + +@@ -72,12 +75,12 @@ bcm2708_fb_set_bitfields(struct fb_var_s + case 2: + case 4: + case 8: +- var->red.length = var->bits_per_pixel; +- var->red.offset = 0; +- var->green.length = var->bits_per_pixel; +- var->green.offset = 0; +- var->blue.length = var->bits_per_pixel; +- var->blue.offset = 0; ++ var->red.length = var->bits_per_pixel; ++ var->red.offset = 0; ++ var->green.length = var->bits_per_pixel; ++ var->green.offset = 0; ++ var->blue.length = var->bits_per_pixel; ++ var->blue.offset = 0; + break; + case 16: + var->red.length = 5; +@@ -89,10 +92,16 @@ bcm2708_fb_set_bitfields(struct fb_var_s + if (var->green.length != 5 && var->green.length != 6) + var->green.length = 6; + break; ++ case 24: ++ var->red.length = 8; ++ var->blue.length = 8; ++ var->green.length = 8; ++ break; + case 32: +- var->red.length = 8; +- var->green.length = 8; +- var->blue.length = 8; ++ var->red.length = 8; ++ var->green.length = 8; ++ var->blue.length = 8; ++ var->transp.length = 8; + break; + default: + ret = -EINVAL; +@@ -104,134 +113,148 @@ bcm2708_fb_set_bitfields(struct fb_var_s + * encoded in the pixel data. Calculate their position from + * the bitfield length defined above. + */ +- if (ret == 0 && var->bits_per_pixel >= 16) { ++ if (ret == 0 && var->bits_per_pixel >= 24) { ++ var->red.offset = 0; ++ var->green.offset = var->red.offset + var->red.length; ++ var->blue.offset = var->green.offset + var->green.length; ++ var->transp.offset = var->blue.offset + var->blue.length; ++ } else if (ret == 0 && var->bits_per_pixel >= 16) { + var->blue.offset = 0; + var->green.offset = var->blue.offset + var->blue.length; + var->red.offset = var->green.offset + var->green.length; ++ var->transp.offset = var->red.offset + var->red.length; + } + + return ret; + } + +-static int bcm2708_fb_check_var(struct fb_var_screeninfo *var, struct fb_info *info) ++static int bcm2708_fb_check_var(struct fb_var_screeninfo *var, ++ struct fb_info *info) + { ++ /* info input, var output */ ++ int yres; ++ /* memory size in pixels */ ++ unsigned pixels = info->screen_size * 8 / var->bits_per_pixel; ++ ++ /* info input, var output */ ++ pr_info("bcm2708_fb_check_var info(%p) %dx%d (%dx%d), %d, %d\n", info, ++ info->var.xres, info->var.yres, info->var.xres_virtual, ++ info->var.yres_virtual, (int)info->screen_size, ++ info->var.bits_per_pixel); ++ pr_info("bcm2708_fb_check_var var(%p) %dx%d (%dx%d), %d, %d\n", var, ++ var->xres, var->yres, var->xres_virtual, var->yres_virtual, ++ var->bits_per_pixel, pixels); ++ ++ if (!var->bits_per_pixel) ++ var->bits_per_pixel = 16; ++ ++ if (bcm2708_fb_set_bitfields(var) != 0) { ++ pr_err("bcm2708_fb_check_var: invalid bits_per_pixel %d\n", ++ var->bits_per_pixel); ++ return -EINVAL; ++ } + +- // info input, var output +- int yres; +- /* memory size in pixels */ +- unsigned pixels = info->screen_size * 8 / var->bits_per_pixel; +- +- // info input, var output +- printk(KERN_ERR "bcm2708_fb_check_var info(%p) %dx%d (%dx%d), %d, %d\n", info, info->var.xres, info->var.yres, info->var.xres_virtual, info->var.yres_virtual, (int)info->screen_size, info->var.bits_per_pixel ); +- printk(KERN_ERR "bcm2708_fb_check_var var(%p) %dx%d (%dx%d), %d, %d\n", var, var->xres, var->yres, var->xres_virtual, var->yres_virtual, var->bits_per_pixel, pixels); +- +- if (!var->bits_per_pixel) var->bits_per_pixel = 16; +- +- if (0 && var->bits_per_pixel != 16 && var->bits_per_pixel != 32) { +- printk(KERN_ERR "bcm2708_fb_check_var: ERROR: bits_per_pixel=%d\n", var->bits_per_pixel); +- return -EINVAL; +- } +- +- bcm2708_fb_set_bitfields(var); +- +- if (var->xres_virtual < var->xres) +- var->xres_virtual = var->xres; +- /* use highest possible virtual resolution */ +- if (var->yres_virtual == -1) { +- var->yres_virtual = 480; //pixels / var->xres_virtual; +- +- printk(KERN_ERR +- "bcm2708_fb_check_var: virtual resolution set to maximum of %dx%d\n", +- var->xres_virtual, var->yres_virtual); +- } +- if (var->yres_virtual < var->yres) +- var->yres_virtual = var->yres; +- +- #if 0 +- if (var->xres_virtual * var->yres_virtual > pixels) { +- printk(KERN_ERR "bcm2708_fb_check_var: mode %dx%dx%d rejected... " +- "virtual resolution too high to fit into video memory!\n", +- var->xres_virtual, var->yres_virtual, +- var->bits_per_pixel); +- return -EINVAL; +- } +- #endif +- if (var->xoffset < 0) +- var->xoffset = 0; +- if (var->yoffset < 0) +- var->yoffset = 0; +- +- /* truncate xoffset and yoffset to maximum if too high */ +- if (var->xoffset > var->xres_virtual - var->xres) +- var->xoffset = var->xres_virtual - var->xres - 1; +- if (var->yoffset > var->yres_virtual - var->yres) +- var->yoffset = var->yres_virtual - var->yres - 1; +- +- var->red.msb_right = +- var->green.msb_right = +- var->blue.msb_right = +- var->transp.offset = +- var->transp.length = +- var->transp.msb_right = 0; +- +- yres = var->yres; +- if (var->vmode & FB_VMODE_DOUBLE) +- yres *= 2; +- else if (var->vmode & FB_VMODE_INTERLACED) +- yres = (yres + 1) / 2; +- +- if (yres > 1200) { +- printk(KERN_ERR "bcm2708_fb_check_var: ERROR: VerticalTotal >= 1200; " +- "special treatment required! (TODO)\n"); +- return -EINVAL; +- } + +- //if (cirrusfb_check_pixclock(var, info)) +- // return -EINVAL; ++ if (var->xres_virtual < var->xres) ++ var->xres_virtual = var->xres; ++ /* use highest possible virtual resolution */ ++ if (var->yres_virtual == -1) { ++ var->yres_virtual = 480; ++ ++ pr_err ++ ("bcm2708_fb_check_var: virtual resolution set to maximum of %dx%d\n", ++ var->xres_virtual, var->yres_virtual); ++ } ++ if (var->yres_virtual < var->yres) ++ var->yres_virtual = var->yres; + +- //if (!is_laguna(cinfo)) +- // var->accel_flags = FB_ACCELF_TEXT; ++ if (var->xoffset < 0) ++ var->xoffset = 0; ++ if (var->yoffset < 0) ++ var->yoffset = 0; ++ ++ /* truncate xoffset and yoffset to maximum if too high */ ++ if (var->xoffset > var->xres_virtual - var->xres) ++ var->xoffset = var->xres_virtual - var->xres - 1; ++ if (var->yoffset > var->yres_virtual - var->yres) ++ var->yoffset = var->yres_virtual - var->yres - 1; ++ ++ yres = var->yres; ++ if (var->vmode & FB_VMODE_DOUBLE) ++ yres *= 2; ++ else if (var->vmode & FB_VMODE_INTERLACED) ++ yres = (yres + 1) / 2; ++ ++ if (yres > 1200) { ++ pr_err("bcm2708_fb_check_var: ERROR: VerticalTotal >= 1200; " ++ "special treatment required! (TODO)\n"); ++ return -EINVAL; ++ } + +- return 0; ++ return 0; + } + + static int bcm2708_fb_set_par(struct fb_info *info) + { +- unsigned val = 0; ++ uint32_t val = 0; + struct bcm2708_fb *fb = to_bcm2708(info); +- volatile struct fbinfo_s *fbinfo = fb->info; +- fbinfo->xres = info->var.xres; +- fbinfo->yres = info->var.yres; +- fbinfo->xres_virtual = info->var.xres_virtual; +- fbinfo->yres_virtual = info->var.yres_virtual; +- fbinfo->bpp = info->var.bits_per_pixel; +- fbinfo->xoffset = info->var.xoffset; +- fbinfo->yoffset = info->var.yoffset; +- fbinfo->base = 0; // filled in by VC +- fbinfo->pitch = 0; // filled in by VC ++ volatile struct fbinfo_s *fbinfo = fb->info; ++ fbinfo->xres = info->var.xres; ++ fbinfo->yres = info->var.yres; ++ fbinfo->xres_virtual = info->var.xres_virtual; ++ fbinfo->yres_virtual = info->var.yres_virtual; ++ fbinfo->bpp = info->var.bits_per_pixel; ++ fbinfo->xoffset = info->var.xoffset; ++ fbinfo->yoffset = info->var.yoffset; ++ fbinfo->base = 0; /* filled in by VC */ ++ fbinfo->pitch = 0; /* filled in by VC */ ++ ++ pr_info("bcm2708_fb_set_par info(%p) %dx%d (%dx%d), %d, %d\n", info, ++ info->var.xres, info->var.yres, info->var.xres_virtual, ++ info->var.yres_virtual, (int)info->screen_size, ++ info->var.bits_per_pixel); + +- printk(KERN_ERR "bcm2708_fb_set_par info(%p) %dx%d (%dx%d), %d, %d\n", info, info->var.xres, info->var.yres, info->var.xres_virtual, info->var.yres_virtual, (int)info->screen_size, info->var.bits_per_pixel ); ++ /* ensure last write to fbinfo is visible to GPU */ ++ wmb(); + +- // inform vc about new framebuffer ++ /* inform vc about new framebuffer */ + bcm_mailbox_write(MBOX_CHAN_FB, fb->dma); + +- // wait for response +- bcm_mailbox_read(MBOX_CHAN_FB, &val); +- +- fb->fb.fix.line_length = fbinfo->pitch; ++ /* TODO: replace fb driver with vchiq version */ ++ /* wait for response */ ++ bcm_mailbox_read(MBOX_CHAN_FB, &val); ++ ++ /* ensure GPU writes are visible to us */ ++ rmb(); ++ ++ if (val == 0) { ++ fb->fb.fix.line_length = fbinfo->pitch; ++ ++ if (info->var.bits_per_pixel <= 8) ++ fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; ++ else ++ fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; ++ ++ fb->fb.fix.smem_start = fbinfo->base; ++ fb->fb.fix.smem_len = fbinfo->pitch * fbinfo->yres_virtual; ++ fb->fb.screen_size = fbinfo->screen_size; ++ if (fb->fb.screen_base) ++ iounmap(fb->fb.screen_base); ++ fb->fb.screen_base = ++ (void *)ioremap_wc(fb->fb.fix.smem_start, fb->fb.screen_size); ++ if (!fb->fb.screen_base) { ++ /* the console may currently be locked */ ++ console_trylock(); ++ console_unlock(); + +- if (info->var.bits_per_pixel <= 8) +- fb->fb.fix.visual = FB_VISUAL_PSEUDOCOLOR; +- else +- fb->fb.fix.visual = FB_VISUAL_TRUECOLOR; +- +- fb->fb.fix.smem_start = fbinfo->base; +- fb->fb.fix.smem_len = fbinfo->pitch * fbinfo->yres_virtual; +- fb->fb.screen_size = fbinfo->screen_size; +- fb->fb.screen_base = (void *)ioremap_nocache(fb->fb.fix.smem_start, fb->fb.screen_size); +- +- printk(KERN_ERR "BCM2708FB: start = %p,%p,%p width=%d, height=%d, bpp=%d, pitch=%d\n", +- (void *)fb->fb.screen_base, (void *)fb->fb.fix.smem_start, (void *)val, fbinfo->xres, fbinfo->yres, fbinfo->bpp, fbinfo->pitch); ++ BUG(); /* what can we do here */ ++ } ++ } ++ pr_info ++ ("BCM2708FB: start = %p,%p width=%d, height=%d, bpp=%d, pitch=%d size=%d success=%d\n", ++ (void *)fb->fb.screen_base, (void *)fb->fb.fix.smem_start, ++ fbinfo->xres, fbinfo->yres, fbinfo->bpp, ++ fbinfo->pitch, (int)fb->fb.screen_size, val); + + return val; + } +@@ -243,58 +266,62 @@ static inline u32 convert_bitfield(int v + return (val >> (16 - bf->length) & mask) << bf->offset; + } + +-static int bcm2708_fb_setcolreg(unsigned int regno, unsigned int red, unsigned int green, +- unsigned int blue, unsigned int transp, struct fb_info *info) ++static int bcm2708_fb_setcolreg(unsigned int regno, unsigned int red, ++ unsigned int green, unsigned int blue, ++ unsigned int transp, struct fb_info *info) + { + struct bcm2708_fb *fb = to_bcm2708(info); + + if (regno < 16) + fb->cmap[regno] = convert_bitfield(transp, &fb->fb.var.transp) | +- convert_bitfield(blue, &fb->fb.var.blue) | +- convert_bitfield(green, &fb->fb.var.green) | +- convert_bitfield(red, &fb->fb.var.red); ++ convert_bitfield(blue, &fb->fb.var.blue) | ++ convert_bitfield(green, &fb->fb.var.green) | ++ convert_bitfield(red, &fb->fb.var.red); + + return regno > 255; + } + + static int bcm2708_fb_blank(int blank_mode, struct fb_info *info) + { +-//printk(KERN_ERR "bcm2708_fb_blank\n"); ++ /*pr_info("bcm2708_fb_blank\n"); */ + return -1; + } + +-static void bcm2708_fb_fillrect(struct fb_info *info, const struct fb_fillrect *rect) ++static void bcm2708_fb_fillrect(struct fb_info *info, ++ const struct fb_fillrect *rect) + { +-// (is called) printk(KERN_ERR "bcm2708_fb_fillrect\n"); ++ /* (is called) pr_info("bcm2708_fb_fillrect\n"); */ + cfb_fillrect(info, rect); + } + +-static void bcm2708_fb_copyarea(struct fb_info *info, const struct fb_copyarea *region) ++static void bcm2708_fb_copyarea(struct fb_info *info, ++ const struct fb_copyarea *region) + { +-//printk(KERN_ERR "bcm2708_fb_copyarea\n"); ++ /*pr_info("bcm2708_fb_copyarea\n"); */ + cfb_copyarea(info, region); + } + +-static void bcm2708_fb_imageblit(struct fb_info *info, const struct fb_image *image) ++static void bcm2708_fb_imageblit(struct fb_info *info, ++ const struct fb_image *image) + { +-// (is called) printk(KERN_ERR "bcm2708_fb_imageblit\n"); ++ /* (is called) pr_info("bcm2708_fb_imageblit\n"); */ + cfb_imageblit(info, image); + } + + static struct fb_ops bcm2708_fb_ops = { +- .owner = THIS_MODULE, +- .fb_check_var = bcm2708_fb_check_var, +- .fb_set_par = bcm2708_fb_set_par, +- .fb_setcolreg = bcm2708_fb_setcolreg, +- .fb_blank = bcm2708_fb_blank, +- .fb_fillrect = bcm2708_fb_fillrect, +- .fb_copyarea = bcm2708_fb_copyarea, +- .fb_imageblit = bcm2708_fb_imageblit, ++ .owner = THIS_MODULE, ++ .fb_check_var = bcm2708_fb_check_var, ++ .fb_set_par = bcm2708_fb_set_par, ++ .fb_setcolreg = bcm2708_fb_setcolreg, ++ .fb_blank = bcm2708_fb_blank, ++ .fb_fillrect = bcm2708_fb_fillrect, ++ .fb_copyarea = bcm2708_fb_copyarea, ++ .fb_imageblit = bcm2708_fb_imageblit, + }; + +-static int FBWIDTH =800; /* module parameter */ +-static int FBHEIGHT =480; /* module parameter */ +- ++static int fbwidth = 800; /* module parameter */ ++static int fbheight = 480; /* module parameter */ ++static int fbdepth = 16; /* module parameter */ + + static int bcm2708_fb_register(struct bcm2708_fb *fb) + { +@@ -302,45 +329,47 @@ static int bcm2708_fb_register(struct bc + dma_addr_t dma; + void *mem; + +- mem = dma_alloc_coherent(NULL, PAGE_ALIGN(sizeof(*fb->info)), &dma, GFP_KERNEL); ++ mem = ++ dma_alloc_coherent(NULL, PAGE_ALIGN(sizeof(*fb->info)), &dma, ++ GFP_KERNEL); + + if (NULL == mem) { +- printk(KERN_ERR ": unable to allocate fbinfo buffer\n"); ++ pr_err(": unable to allocate fbinfo buffer\n"); + ret = -ENOMEM; + } else { + fb->info = (struct fbinfo_s *)mem; +- fb->dma = dma; +- } +- fb->fb.fbops = &bcm2708_fb_ops; +- fb->fb.flags = FBINFO_FLAG_DEFAULT | FBINFO_HWACCEL_COPYAREA | FBINFO_HWACCEL_FILLRECT | FBINFO_HWACCEL_IMAGEBLIT; +- fb->fb.pseudo_palette = fb->cmap; ++ fb->dma = dma; ++ } ++ fb->fb.fbops = &bcm2708_fb_ops; ++ fb->fb.flags = FBINFO_FLAG_DEFAULT; ++ fb->fb.pseudo_palette = fb->cmap; + + strncpy(fb->fb.fix.id, bcm2708_name, sizeof(fb->fb.fix.id)); +- fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; +- fb->fb.fix.type_aux = 0; +- fb->fb.fix.xpanstep = 0; +- fb->fb.fix.ypanstep = 0; +- fb->fb.fix.ywrapstep = 0; +- fb->fb.fix.accel = FB_ACCEL_NONE; +- +- fb->fb.var.xres = FBWIDTH; +- fb->fb.var.yres = FBHEIGHT; +- fb->fb.var.xres_virtual = FBWIDTH; +- fb->fb.var.yres_virtual = FBHEIGHT; +- fb->fb.var.bits_per_pixel = 16; +- fb->fb.var.vmode = FB_VMODE_NONINTERLACED; +- fb->fb.var.activate = FB_ACTIVATE_NOW; +- fb->fb.var.nonstd = 0; +- fb->fb.var.height = FBWIDTH; +- fb->fb.var.width = FBHEIGHT; +- fb->fb.var.accel_flags = 0; +- +- fb->fb.monspecs.hfmin = 0; +- fb->fb.monspecs.hfmax = 100000; +- fb->fb.monspecs.vfmin = 0; +- fb->fb.monspecs.vfmax = 400; ++ fb->fb.fix.type = FB_TYPE_PACKED_PIXELS; ++ fb->fb.fix.type_aux = 0; ++ fb->fb.fix.xpanstep = 0; ++ fb->fb.fix.ypanstep = 0; ++ fb->fb.fix.ywrapstep = 0; ++ fb->fb.fix.accel = FB_ACCEL_NONE; ++ ++ fb->fb.var.xres = fbwidth; ++ fb->fb.var.yres = fbheight; ++ fb->fb.var.xres_virtual = fbwidth; ++ fb->fb.var.yres_virtual = fbheight; ++ fb->fb.var.bits_per_pixel = fbdepth; ++ fb->fb.var.vmode = FB_VMODE_NONINTERLACED; ++ fb->fb.var.activate = FB_ACTIVATE_NOW; ++ fb->fb.var.nonstd = 0; ++ fb->fb.var.height = fbwidth; ++ fb->fb.var.width = fbheight; ++ fb->fb.var.accel_flags = 0; ++ ++ fb->fb.monspecs.hfmin = 0; ++ fb->fb.monspecs.hfmax = 100000; ++ fb->fb.monspecs.vfmin = 0; ++ fb->fb.monspecs.vfmax = 400; + fb->fb.monspecs.dclkmin = 1000000; +- fb->fb.monspecs.dclkmax = 100000000; ++ fb->fb.monspecs.dclkmax = 100000000; + + bcm2708_fb_set_bitfields(&fb->fb.var); + +@@ -350,17 +379,16 @@ static int bcm2708_fb_register(struct bc + + fb_set_var(&fb->fb, &fb->fb.var); + +- printk(KERN_INFO "BCM2708FB: registering framebuffer (%d, %d)\n", FBWIDTH, FBHEIGHT); ++ pr_info("BCM2708FB: registering framebuffer (%dx%d@%d)\n", fbwidth, ++ fbheight, fbdepth); + + ret = register_framebuffer(&fb->fb); +- printk(KERN_ERR "BCM2708FB: register framebuffer (%d)\n", ret); ++ pr_info("BCM2708FB: register framebuffer (%d)\n", ret); + if (ret == 0) + goto out; + +- printk(KERN_ERR "BCM2708FB: cannot register framebuffer (%d)\n", ret); +- +- iounmap(fb->regs); +- out: ++ pr_info("BCM2708FB: cannot register framebuffer (%d)\n", ret); ++out: + return ret; + } + +@@ -371,7 +399,8 @@ static int bcm2708_fb_probe(struct platf + + fb = kmalloc(sizeof(struct bcm2708_fb), GFP_KERNEL); + if (!fb) { +- dev_err(&dev->dev, "could not allocate new bcm2708_fb struct\n"); ++ dev_err(&dev->dev, ++ "could not allocate new bcm2708_fb struct\n"); + ret = -ENOMEM; + goto free_region; + } +@@ -386,9 +415,9 @@ static int bcm2708_fb_probe(struct platf + } + + kfree(fb); +- free_region: ++free_region: + dev_err(&dev->dev, "probe failed, err %d\n", ret); +- out: ++out: + return ret; + } + +@@ -398,22 +427,24 @@ static int bcm2708_fb_remove(struct plat + + platform_set_drvdata(dev, NULL); + ++ if (fb->fb.screen_base) ++ iounmap(fb->fb.screen_base); + unregister_framebuffer(&fb->fb); +- iounmap(fb->regs); + +- dma_free_coherent(NULL, PAGE_ALIGN(sizeof(*fb->info)), (void *)fb->info, fb->dma); ++ dma_free_coherent(NULL, PAGE_ALIGN(sizeof(*fb->info)), (void *)fb->info, ++ fb->dma); + kfree(fb); + + return 0; + } + + static struct platform_driver bcm2708_fb_driver = { +- .probe = bcm2708_fb_probe, +- .remove = bcm2708_fb_remove, +- .driver = { +- .name = DRIVER_NAME, +- .owner = THIS_MODULE, +- }, ++ .probe = bcm2708_fb_probe, ++ .remove = bcm2708_fb_remove, ++ .driver = { ++ .name = DRIVER_NAME, ++ .owner = THIS_MODULE, ++ }, + }; + + static int __init bcm2708_fb_init(void) +@@ -430,11 +461,13 @@ static void __exit bcm2708_fb_exit(void) + + module_exit(bcm2708_fb_exit); + +-module_param(FBWIDTH, int, 0644); +-module_param(FBHEIGHT, int, 0644); ++module_param(fbwidth, int, 0644); ++module_param(fbheight, int, 0644); ++module_param(fbdepth, int, 0644); + + MODULE_DESCRIPTION("BCM2708 framebuffer driver"); + MODULE_LICENSE("GPL"); + +-MODULE_PARM_DESC(FBWIDTH, "Width of ARM Framebuffer"); +-MODULE_PARM_DESC(FBHEIGHT, "Height of ARM Framebuffer"); ++MODULE_PARM_DESC(fbwidth, "Width of ARM Framebuffer"); ++MODULE_PARM_DESC(fbheight, "Height of ARM Framebuffer"); ++MODULE_PARM_DESC(fbdepth, "Bit depth of ARM Framebuffer"); +--- a/sound/arm/Kconfig ++++ b/sound/arm/Kconfig +@@ -39,5 +39,12 @@ config SND_PXA2XX_AC97 + Say Y or M if you want to support any AC97 codec attached to + the PXA2xx AC97 interface. + ++config SND_BCM2835 ++ tristate "BCM2835 ALSA driver" ++ depends on ARCH_BCM2708 && SND ++ select SND_PCM ++ help ++ Say Y or M if you want to support BCM2835 Alsa pcm card driver ++ + endif # SND_ARM + +--- a/sound/arm/Makefile ++++ b/sound/arm/Makefile +@@ -14,3 +14,9 @@ snd-pxa2xx-lib-$(CONFIG_SND_PXA2XX_LIB_A + + obj-$(CONFIG_SND_PXA2XX_AC97) += snd-pxa2xx-ac97.o + snd-pxa2xx-ac97-objs := pxa2xx-ac97.o ++ ++obj-$(CONFIG_SND_BCM2835) += snd-bcm2835.o ++snd-bcm2835-objs := bcm2835.o bcm2835-ctl.o bcm2835-pcm.o bcm2835-vchiq.o ++ ++EXTRA_CFLAGS += -Idrivers/misc/vc04_services -Idrivers/misc/vc04_services/interface/vcos/linuxkernel -D__VCCOREVER__=0x04000000 ++ +--- /dev/null ++++ b/sound/arm/bcm2835-ctl.c +@@ -0,0 +1,172 @@ ++/***************************************************************************** ++* Copyright 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include <linux/platform_device.h> ++#include <linux/init.h> ++#include <linux/io.h> ++#include <linux/jiffies.h> ++#include <linux/slab.h> ++#include <linux/time.h> ++#include <linux/wait.h> ++#include <linux/delay.h> ++#include <linux/moduleparam.h> ++#include <linux/sched.h> ++ ++#include <sound/core.h> ++#include <sound/control.h> ++#include <sound/pcm.h> ++#include <sound/pcm_params.h> ++#include <sound/rawmidi.h> ++#include <sound/initval.h> ++#include <sound/tlv.h> ++ ++#include "bcm2835.h" ++ ++static int snd_bcm2835_ctl_info(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_info *uinfo) ++{ ++ if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ uinfo->value.integer.min = -10240; ++ uinfo->value.integer.max = 2303; ++ } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) { ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = 1; ++ } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) { ++ uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER; ++ uinfo->count = 1; ++ uinfo->value.integer.min = 0; ++ uinfo->value.integer.max = AUDIO_DEST_MAX-0; ++ } ++ ++ return 0; ++} ++ ++static int snd_bcm2835_ctl_get(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); ++ ++ BUG_ON(!chip && !(chip->avail_substreams & AVAIL_SUBSTREAMS_MASK)); ++ ++ if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) ++ ucontrol->value.integer.value[0] = chip->volume; ++ else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) ++ ucontrol->value.integer.value[0] = chip->mute; ++ else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) ++ ucontrol->value.integer.value[0] = chip->dest; ++ ++ return 0; ++} ++ ++static int snd_bcm2835_ctl_put(struct snd_kcontrol *kcontrol, ++ struct snd_ctl_elem_value *ucontrol) ++{ ++ struct bcm2835_chip *chip = snd_kcontrol_chip(kcontrol); ++ int changed = 0; ++ ++ if (kcontrol->private_value == PCM_PLAYBACK_VOLUME) { ++ if (chip->mute) { ++ chip->mute = 0; ++ changed = 1; ++ } ++ if (changed ++ || (ucontrol->value.integer.value[0] != chip->volume)) { ++ int atten; ++ ++ chip->volume = ucontrol->value.integer.value[0]; ++ changed = 1; ++ atten = -((chip->volume << 8) / 100); ++ chip->volume = atten; ++ } ++ ++ } else if (kcontrol->private_value == PCM_PLAYBACK_MUTE) { ++ /* Not implemented */ ++ if (ucontrol->value.integer.value[0] != chip->mute) { ++ chip->mute = ucontrol->value.integer.value[0]; ++ changed = 0; ++ } ++ } else if (kcontrol->private_value == PCM_PLAYBACK_DEVICE) { ++ if (ucontrol->value.integer.value[0] != chip->dest) { ++ chip->dest = ucontrol->value.integer.value[0]; ++ changed = 1; ++ } ++ } ++ ++ if (changed) { ++ if (bcm2835_audio_set_ctls(chip)) ++ printk(KERN_ERR "Failed to set ALSA controls..\n"); ++ } ++ ++ return changed; ++} ++ ++static DECLARE_TLV_DB_SCALE(snd_bcm2835_db_scale, -10240, 1, 1); ++ ++static struct snd_kcontrol_new snd_bcm2835_ctl[] __devinitdata = { ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "PCM Playback Volume", ++ .index = 0, ++ .access = ++ SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READWRITE, ++ .private_value = PCM_PLAYBACK_VOLUME, ++ .info = snd_bcm2835_ctl_info, ++ .get = snd_bcm2835_ctl_get, ++ .put = snd_bcm2835_ctl_put, ++ .count = 1, ++ .tlv = {.p = snd_bcm2835_db_scale} ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "PCM Playback Switch", ++ .index = 0, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, ++ .private_value = PCM_PLAYBACK_MUTE, ++ .info = snd_bcm2835_ctl_info, ++ .get = snd_bcm2835_ctl_get, ++ .put = snd_bcm2835_ctl_put, ++ .count = 1, ++ }, ++ { ++ .iface = SNDRV_CTL_ELEM_IFACE_MIXER, ++ .name = "PCM Playback Route", ++ .index = 0, ++ .access = SNDRV_CTL_ELEM_ACCESS_READWRITE, ++ .private_value = PCM_PLAYBACK_DEVICE, ++ .info = snd_bcm2835_ctl_info, ++ .get = snd_bcm2835_ctl_get, ++ .put = snd_bcm2835_ctl_put, ++ .count = 1, ++ }, ++}; ++ ++int __devinit snd_bcm2835_new_ctl(bcm2835_chip_t * chip) ++{ ++ int err; ++ unsigned int idx; ++ ++ strcpy(chip->card->mixername, "Broadcom Mixer"); ++ for (idx = 0; idx < ARRAY_SIZE(snd_bcm2835_ctl); idx++) { ++ err = ++ snd_ctl_add(chip->card, ++ snd_ctl_new1(&snd_bcm2835_ctl[idx], chip)); ++ if (err < 0) ++ return err; ++ } ++ return 0; ++} +--- /dev/null ++++ b/sound/arm/bcm2835-pcm.c +@@ -0,0 +1,424 @@ ++/***************************************************************************** ++* Copyright 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include <linux/interrupt.h> ++#include <linux/slab.h> ++ ++#include "bcm2835.h" ++ ++/* hardware definition */ ++static struct snd_pcm_hardware snd_bcm2835_playback_hw = { ++ .info = (SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER), ++ .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, ++ .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, ++ .rate_min = 8000, ++ .rate_max = 48000, ++ .channels_min = 1, ++ .channels_max = 2, ++ .buffer_bytes_max = (4 * 8 - 1) * 1024, /* Needs to be less than audioplay buffer size */ ++ .period_bytes_min = 1 * 1024, ++ .period_bytes_max = (4 * 8 - 1) * 1024, ++ .periods_min = 1, ++ .periods_max = 4 * 8 - 1, ++}; ++ ++static void snd_bcm2835_playback_free(struct snd_pcm_runtime *runtime) ++{ ++ audio_info("Freeing up alsa stream here ..\n"); ++ if (runtime->private_data) ++ kfree(runtime->private_data); ++ runtime->private_data = NULL; ++} ++ ++static irqreturn_t bcm2835_playback_fifo_irq(int irq, void *dev_id) ++{ ++ bcm2835_alsa_stream_t *alsa_stream = (bcm2835_alsa_stream_t *) dev_id; ++ uint32_t consumed = 0; ++ int new_period = 0; ++ ++ audio_info(" .. IN\n"); ++ ++ audio_info("alsa_stream=%p substream=%p\n", alsa_stream, ++ alsa_stream ? alsa_stream->substream : 0); ++ ++ if (alsa_stream->open) ++ consumed = bcm2835_audio_retrieve_buffers(alsa_stream); ++ ++ /* We get called only if playback was triggered, So, the number of buffers we retrieve in ++ * each iteration are the buffers that have been played out already ++ */ ++ ++ if (alsa_stream->period_size) { ++ if ((alsa_stream->pos / alsa_stream->period_size) != ++ ((alsa_stream->pos + consumed) / alsa_stream->period_size)) ++ new_period = 1; ++ } ++ audio_debug("updating pos cur: %d + %d max:%d new_period:%d\n", ++ alsa_stream->pos, ++ (consumed /** AUDIO_IPC_BLOCK_BUFFER_SIZE*/ ), ++ alsa_stream->buffer_size, new_period); ++ if (alsa_stream->buffer_size) { ++ alsa_stream->pos += consumed; ++ alsa_stream->pos %= alsa_stream->buffer_size; ++ } ++ if (alsa_stream->substream) { ++ if (new_period) ++ snd_pcm_period_elapsed(alsa_stream->substream); ++ } else { ++ audio_warning(" unexpected NULL substream\n"); ++ } ++ audio_info(" .. OUT\n"); ++ ++ return IRQ_HANDLED; ++} ++ ++/* open callback */ ++static int snd_bcm2835_playback_open(struct snd_pcm_substream *substream) ++{ ++ bcm2835_chip_t *chip = snd_pcm_substream_chip(substream); ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream; ++ int idx; ++ int err; ++ ++ audio_info(" .. IN (%d)\n", substream->number); ++ ++ audio_warning("Alsa open (%d)\n", substream->number); ++ idx = substream->number; ++ ++ if (idx > MAX_SUBSTREAMS) { ++ audio_error ++ ("substream(%d) device doesn't exist max(%d) substreams allowed\n", ++ idx, MAX_SUBSTREAMS); ++ err = -ENODEV; ++ goto out; ++ } ++ ++ /* Check if we are ready */ ++ if (!(chip->avail_substreams & (1 << idx))) { ++ /* We are not ready yet */ ++ audio_error("substream(%d) device is not ready yet\n", idx); ++ err = -EAGAIN; ++ goto out; ++ } ++ ++ alsa_stream = kzalloc(sizeof(bcm2835_alsa_stream_t), GFP_KERNEL); ++ if (alsa_stream == NULL) { ++ return -ENOMEM; ++ } ++ ++ /* Initialise alsa_stream */ ++ alsa_stream->chip = chip; ++ alsa_stream->substream = substream; ++ alsa_stream->idx = idx; ++ chip->alsa_stream[idx] = alsa_stream; ++ ++ sema_init(&alsa_stream->buffers_update_sem, 0); ++ sema_init(&alsa_stream->control_sem, 0); ++ spin_lock_init(&alsa_stream->lock); ++ ++ /* Enabled in start trigger, called on each "fifo irq" after that */ ++ alsa_stream->enable_fifo_irq = 0; ++ alsa_stream->fifo_irq_handler = bcm2835_playback_fifo_irq; ++ ++ runtime->private_data = alsa_stream; ++ runtime->private_free = snd_bcm2835_playback_free; ++ runtime->hw = snd_bcm2835_playback_hw; ++ ++ /* minimum 16 bytes alignment (for vchiq bulk transfers) */ ++ snd_pcm_hw_constraint_step(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_BYTES, ++ 16); ++ ++ err = bcm2835_audio_open(alsa_stream); ++ if (err != 0) { ++ kfree(alsa_stream); ++ return err; ++ } ++ ++ alsa_stream->open = 1; ++ alsa_stream->draining = 1; ++ ++out: ++ audio_info(" .. OUT =%d\n", err); ++ ++ return err; ++} ++ ++/* close callback */ ++static int snd_bcm2835_playback_close(struct snd_pcm_substream *substream) ++{ ++ /* the hardware-specific codes will be here */ ++ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ ++ audio_info(" .. IN\n"); ++ audio_warning("Alsa close\n"); ++ ++ /* ++ * Call stop if it's still running. This happens when app ++ * is force killed and we don't get a stop trigger. ++ */ ++ if (alsa_stream->running) { ++ int err; ++ err = bcm2835_audio_stop(alsa_stream); ++ alsa_stream->running = 0; ++ if (err != 0) ++ audio_error(" Failed to STOP alsa device\n"); ++ } ++ ++ alsa_stream->period_size = 0; ++ alsa_stream->buffer_size = 0; ++ ++ if (alsa_stream->open) { ++ alsa_stream->open = 0; ++ bcm2835_audio_close(alsa_stream); ++ } ++ if (alsa_stream->chip) ++ alsa_stream->chip->alsa_stream[alsa_stream->idx] = NULL; ++ /* ++ * Do not free up alsa_stream here, it will be freed up by ++ * runtime->private_free callback we registered in *_open above ++ */ ++ ++ audio_info(" .. OUT\n"); ++ ++ return 0; ++} ++ ++/* hw_params callback */ ++static int snd_bcm2835_pcm_hw_params(struct snd_pcm_substream *substream, ++ struct snd_pcm_hw_params *params) ++{ ++ int err; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = ++ (bcm2835_alsa_stream_t *) runtime->private_data; ++ ++ audio_info(" .. IN\n"); ++ ++ err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params)); ++ if (err < 0) { ++ audio_error ++ (" pcm_lib_malloc failed to allocated pages for buffers\n"); ++ return err; ++ } ++ ++ err = bcm2835_audio_set_params(alsa_stream, params_channels(params), ++ params_rate(params), ++ snd_pcm_format_width(params_format ++ (params))); ++ if (err < 0) { ++ audio_error(" error setting hw params\n"); ++ } ++ ++ bcm2835_audio_setup(alsa_stream); ++ audio_info(" .. OUT\n"); ++ ++ return err; ++} ++ ++/* hw_free callback */ ++static int snd_bcm2835_pcm_hw_free(struct snd_pcm_substream *substream) ++{ ++ audio_info(" .. IN\n"); ++ return snd_pcm_lib_free_pages(substream); ++} ++ ++/* prepare callback */ ++static int snd_bcm2835_pcm_prepare(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ ++ audio_info(" .. IN\n"); ++ ++ alsa_stream->buffer_size = snd_pcm_lib_buffer_bytes(substream); ++ alsa_stream->period_size = snd_pcm_lib_period_bytes(substream); ++ alsa_stream->pos = 0; ++ ++ audio_debug("buffer_size=%d, period_size=%d pos=%d frame_bits=%d\n", ++ alsa_stream->buffer_size, alsa_stream->period_size, ++ alsa_stream->pos, runtime->frame_bits); ++ ++ audio_info(" .. OUT\n"); ++ return 0; ++} ++ ++/* trigger callback */ ++static int snd_bcm2835_pcm_trigger(struct snd_pcm_substream *substream, int cmd) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ int err = 0; ++ ++ audio_info(" .. IN\n"); ++ ++ switch (cmd) { ++ case SNDRV_PCM_TRIGGER_START: ++ audio_debug("bcm2835_AUDIO_TRIGGER_START running=%d\n", ++ alsa_stream->running); ++ if (!alsa_stream->running) { ++ err = bcm2835_audio_start(alsa_stream); ++ if (err == 0) { ++ alsa_stream->running = 1; ++ alsa_stream->draining = 1; ++ } ++ } ++ break; ++ case SNDRV_PCM_TRIGGER_STOP: ++ audio_debug ++ ("bcm2835_AUDIO_TRIGGER_STOP running=%d draining=%d\n", ++ runtime->status->state == SNDRV_PCM_STATE_DRAINING, ++ alsa_stream->running); ++ if (runtime->status->state == SNDRV_PCM_STATE_DRAINING) { ++ audio_info("DRAINING\n"); ++ alsa_stream->draining = 1; ++ } else { ++ audio_info("DROPPING\n"); ++ alsa_stream->draining = 0; ++ } ++ if (alsa_stream->running) { ++ err = bcm2835_audio_stop(alsa_stream); ++ if (err != 0) ++ audio_error(" Failed to STOP alsa device\n"); ++ alsa_stream->running = 0; ++ } ++ break; ++ default: ++ err = -EINVAL; ++ } ++ ++ audio_info(" .. OUT\n"); ++ return err; ++} ++ ++/* pointer callback */ ++static snd_pcm_uframes_t ++snd_bcm2835_pcm_pointer(struct snd_pcm_substream *substream) ++{ ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ ++ audio_info(" .. IN\n"); ++ ++ audio_debug("pcm_pointer... (%d) hwptr=%d appl=%d pos=%d\n", 0, ++ frames_to_bytes(runtime, runtime->status->hw_ptr), ++ frames_to_bytes(runtime, runtime->control->appl_ptr), ++ alsa_stream->pos); ++ ++ audio_info(" .. OUT\n"); ++ return bytes_to_frames(runtime, alsa_stream->pos); ++} ++ ++static int snd_bcm2835_pcm_copy(struct snd_pcm_substream *substream, ++ int channel, snd_pcm_uframes_t pos, void *src, ++ snd_pcm_uframes_t count) ++{ ++ int ret; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ ++ audio_info(" .. IN\n"); ++ audio_debug("copy.......... (%d) hwptr=%d appl=%d pos=%d\n", ++ frames_to_bytes(runtime, count), frames_to_bytes(runtime, ++ runtime-> ++ status-> ++ hw_ptr), ++ frames_to_bytes(runtime, runtime->control->appl_ptr), ++ alsa_stream->pos); ++ ret = ++ bcm2835_audio_write(alsa_stream, frames_to_bytes(runtime, count), ++ src); ++ audio_info(" .. OUT\n"); ++ return ret; ++} ++ ++static int snd_bcm2835_pcm_silence(struct snd_pcm_substream *substream, ++ int channel, snd_pcm_uframes_t post, ++ snd_pcm_uframes_t count) ++{ ++ int ret; ++ struct snd_pcm_runtime *runtime = substream->runtime; ++ bcm2835_alsa_stream_t *alsa_stream = runtime->private_data; ++ ++ audio_info(" .. IN\n"); ++ audio_debug("silence....... (%d) hwptr=%d appl=%d pos=%d\n", ++ frames_to_bytes(runtime, count), frames_to_bytes(runtime, ++ runtime-> ++ status-> ++ hw_ptr), ++ frames_to_bytes(runtime, runtime->control->appl_ptr), ++ alsa_stream->pos); ++ ret = ++ bcm2835_audio_write(alsa_stream, frames_to_bytes(runtime, count), ++ NULL); ++ audio_info(" .. OUT\n"); ++ return ret; ++} ++ ++static int snd_bcm2835_pcm_lib_ioctl(struct snd_pcm_substream *substream, ++ unsigned int cmd, void *arg) ++{ ++ int ret = snd_pcm_lib_ioctl(substream, cmd, arg); ++ audio_info(" .. substream=%p, cmd=%d, arg=%p (%x) ret=%d\n", substream, ++ cmd, arg, arg ? *(unsigned *)arg : 0, ret); ++ return ret; ++} ++ ++/* operators */ ++static struct snd_pcm_ops snd_bcm2835_playback_ops = { ++ .open = snd_bcm2835_playback_open, ++ .close = snd_bcm2835_playback_close, ++ .ioctl = snd_bcm2835_pcm_lib_ioctl, ++ .hw_params = snd_bcm2835_pcm_hw_params, ++ .hw_free = snd_bcm2835_pcm_hw_free, ++ .prepare = snd_bcm2835_pcm_prepare, ++ .trigger = snd_bcm2835_pcm_trigger, ++ .pointer = snd_bcm2835_pcm_pointer, ++ .copy = snd_bcm2835_pcm_copy, ++ .silence = snd_bcm2835_pcm_silence, ++}; ++ ++/* create a pcm device */ ++int __devinit snd_bcm2835_new_pcm(bcm2835_chip_t * chip) ++{ ++ struct snd_pcm *pcm; ++ int err; ++ ++ audio_info(" .. IN\n"); ++ err = ++ snd_pcm_new(chip->card, "bcm2835 ALSA", 0, MAX_SUBSTREAMS, 0, &pcm); ++ if (err < 0) ++ return err; ++ pcm->private_data = chip; ++ strcpy(pcm->name, "bcm2835 ALSA"); ++ chip->pcm = pcm; ++ chip->dest = AUDIO_DEST_AUTO; ++ chip->volume = 100; ++ /* set operators */ ++ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, ++ &snd_bcm2835_playback_ops); ++ ++ /* pre-allocation of buffers */ ++ /* NOTE: this may fail */ ++ snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS, ++ snd_dma_continuous_data ++ (GFP_KERNEL), 64 * 1024, ++ 64 * 1024); ++ ++ audio_info(" .. OUT\n"); ++ ++ return 0; ++} +--- /dev/null ++++ b/sound/arm/bcm2835-vchiq.c +@@ -0,0 +1,818 @@ ++/***************************************************************************** ++* Copyright 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include <linux/device.h> ++#include <sound/core.h> ++#include <sound/initval.h> ++#include <sound/pcm.h> ++#include <linux/io.h> ++#include <linux/interrupt.h> ++#include <linux/fs.h> ++#include <linux/file.h> ++#include <linux/mm.h> ++#include <linux/syscalls.h> ++#include <asm/uaccess.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/atomic.h> ++ ++#include "bcm2835.h" ++ ++/* ---- Include Files -------------------------------------------------------- */ ++ ++#include "interface/vchi/vchi.h" ++#include "interface/vcos/vcos.h" ++#include "interface/vcos/vcos_logging.h" ++#include "vc_vchi_audioserv_defs.h" ++ ++/* ---- Private Constants and Types ------------------------------------------ */ ++ ++/* VCOS logging category for this service */ ++#define VCOS_LOG_CATEGORY (&audio_log_category) ++ ++/* Default VCOS logging level */ ++#define LOG_LEVEL VCOS_LOG_WARN ++ ++/* Logging macros (for remapping to other logging mechanisms, i.e., printf) */ ++#define LOG_ERR( fmt, arg... ) vcos_log_error( "%s:%d " fmt, __func__, __LINE__, ##arg) ++#define LOG_WARN( fmt, arg... ) vcos_log_warn( "%s:%d " fmt, __func__, __LINE__, ##arg) ++#define LOG_INFO( fmt, arg... ) vcos_log_info( "%s:%d " fmt, __func__, __LINE__, ##arg) ++#define LOG_DBG( fmt, arg... ) vcos_log_info( "%s:%d " fmt, __func__, __LINE__, ##arg) ++ ++typedef struct opaque_AUDIO_INSTANCE_T { ++ uint32_t num_connections; ++ VCHI_SERVICE_HANDLE_T vchi_handle[VCHI_MAX_NUM_CONNECTIONS]; ++ VCOS_EVENT_T msg_avail_event; ++ VCOS_MUTEX_T vchi_mutex; ++ bcm2835_alsa_stream_t *alsa_stream; ++ int32_t result, got_result; ++} AUDIO_INSTANCE_T; ++ ++/* ---- Private Variables ---------------------------------------------------- */ ++ ++/* VCOS logging category for this service */ ++static VCOS_LOG_CAT_T audio_log_category; ++ ++/* ---- Private Function Prototypes ------------------------------------------ */ ++ ++/* ---- Private Functions ---------------------------------------------------- */ ++ ++static int bcm2835_audio_stop_worker(bcm2835_alsa_stream_t * alsa_stream); ++static int bcm2835_audio_start_worker(bcm2835_alsa_stream_t * alsa_stream); ++ ++typedef struct { ++ struct work_struct my_work; ++ bcm2835_alsa_stream_t *alsa_stream; ++ int x; ++} my_work_t; ++ ++static void my_wq_function(struct work_struct *work) ++{ ++ my_work_t *w = (my_work_t *) work; ++ int ret = -9; ++ LOG_DBG(" .. IN %p:%d\n", w->alsa_stream, w->x); ++ switch (w->x) { ++ case 1: ++ ret = bcm2835_audio_start_worker(w->alsa_stream); ++ break; ++ case 2: ++ ret = bcm2835_audio_stop_worker(w->alsa_stream); ++ break; ++ default: ++ LOG_ERR(" Unexpected work: %p:%d\n", w->alsa_stream, w->x); ++ break; ++ } ++ kfree((void *)work); ++ LOG_DBG(" .. OUT %d\n", ret); ++} ++ ++int bcm2835_audio_start(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ int ret = -1; ++ LOG_DBG(" .. IN\n"); ++ if (alsa_stream->my_wq) { ++ my_work_t *work = kmalloc(sizeof(my_work_t), GFP_KERNEL); ++ /* Queue some work (item 1) */ ++ if (work) { ++ INIT_WORK((struct work_struct *)work, my_wq_function); ++ work->alsa_stream = alsa_stream; ++ work->x = 1; ++ if (queue_work ++ (alsa_stream->my_wq, (struct work_struct *)work)) ++ ret = 0; ++ } else ++ LOG_ERR(" .. Error: NULL work kmalloc\n"); ++ } ++ LOG_DBG(" .. OUT %d\n", ret); ++ return ret; ++} ++ ++int bcm2835_audio_stop(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ int ret = -1; ++ LOG_DBG(" .. IN\n"); ++ if (alsa_stream->my_wq) { ++ my_work_t *work = kmalloc(sizeof(my_work_t), GFP_KERNEL); ++ /* Queue some work (item 1) */ ++ if (work) { ++ INIT_WORK((struct work_struct *)work, my_wq_function); ++ work->alsa_stream = alsa_stream; ++ work->x = 2; ++ if (queue_work ++ (alsa_stream->my_wq, (struct work_struct *)work)) ++ ret = 0; ++ } else ++ LOG_ERR(" .. Error: NULL work kmalloc\n"); ++ } ++ LOG_DBG(" .. OUT %d\n", ret); ++ return ret; ++} ++ ++void my_workqueue_init(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ alsa_stream->my_wq = create_workqueue("my_queue"); ++} ++ ++void my_workqueue_quit(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ if (alsa_stream->my_wq) { ++ flush_workqueue(alsa_stream->my_wq); ++ destroy_workqueue(alsa_stream->my_wq); ++ alsa_stream->my_wq = NULL; ++ } ++} ++ ++static void audio_vchi_callback(void *param, ++ const VCHI_CALLBACK_REASON_T reason, ++ void *msg_handle) ++{ ++ AUDIO_INSTANCE_T *instance = (AUDIO_INSTANCE_T *) param; ++ int32_t status; ++ int32_t msg_len; ++ VC_AUDIO_MSG_T m; ++ bcm2835_alsa_stream_t *alsa_stream = 0; ++ LOG_DBG(" .. IN instance=%p, param=%p, reason=%d, handle=%p\n", ++ instance, param, reason, msg_handle); ++ ++ if (!instance || reason != VCHI_CALLBACK_MSG_AVAILABLE) { ++ return; ++ } ++ alsa_stream = instance->alsa_stream; ++ status = vchi_msg_dequeue(instance->vchi_handle[0], ++ &m, sizeof m, &msg_len, VCHI_FLAGS_NONE); ++ if (m.type == VC_AUDIO_MSG_TYPE_RESULT) { ++ LOG_DBG ++ (" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_RESULT, success=%d\n", ++ instance, m.u.result.success); ++ BUG_ON(instance->got_result); ++ instance->result = m.u.result.success; ++ instance->got_result = 1; ++ vcos_event_signal(&instance->msg_avail_event); ++ } else if (m.type == VC_AUDIO_MSG_TYPE_COMPLETE) { ++ irq_handler_t callback = (irq_handler_t) m.u.complete.callback; ++ LOG_DBG ++ (" .. instance=%p, m.type=VC_AUDIO_MSG_TYPE_COMPLETE, complete=%d\n", ++ instance, m.u.complete.count); ++ if (alsa_stream && callback) { ++ atomic_add(m.u.complete.count, &alsa_stream->retrieved); ++ callback(0, alsa_stream); ++ } else { ++ LOG_DBG(" .. unexpected alsa_stream=%p, callback=%p\n", ++ alsa_stream, callback); ++ } ++ vcos_event_signal(&instance->msg_avail_event); ++ } else { ++ LOG_DBG(" .. unexpected m.type=%d\n", m.type); ++ } ++} ++ ++static AUDIO_INSTANCE_T *vc_vchi_audio_init(VCHI_INSTANCE_T vchi_instance, ++ VCHI_CONNECTION_T ** ++ vchi_connections, ++ uint32_t num_connections) ++{ ++ uint32_t i; ++ AUDIO_INSTANCE_T *instance; ++ VCOS_STATUS_T status; ++ ++ LOG_DBG("%s: start", __func__); ++ ++ if (num_connections > VCHI_MAX_NUM_CONNECTIONS) { ++ LOG_ERR("%s: unsupported number of connections %u (max=%u)", ++ __func__, num_connections, VCHI_MAX_NUM_CONNECTIONS); ++ ++ return NULL; ++ } ++ /* Allocate memory for this instance */ ++ instance = vcos_malloc(sizeof(*instance), "audio_instance"); ++ memset(instance, 0, sizeof(*instance)); ++ ++ instance->num_connections = num_connections; ++ /* Create the message available event */ ++ status = ++ vcos_event_create(&instance->msg_avail_event, "audio_msg_avail"); ++ if (status != VCOS_SUCCESS) { ++ LOG_ERR("%s: failed to create event (status=%d)", __func__, ++ status); ++ ++ goto err_free_mem; ++ } ++ /* Create a lock for exclusive, serialized VCHI connection access */ ++ status = vcos_mutex_create(&instance->vchi_mutex, "audio_vchi_mutex"); ++ if (status != VCOS_SUCCESS) { ++ LOG_ERR("%s: failed to create event (status=%d)", __func__, ++ status); ++ ++ goto err_delete_event; ++ } ++ /* Open the VCHI service connections */ ++ for (i = 0; i < num_connections; i++) { ++ SERVICE_CREATION_T params = { ++ VC_AUDIO_SERVER_NAME, // 4cc service code ++ vchi_connections[i], // passed in fn pointers ++ 0, // rx fifo size (unused) ++ 0, // tx fifo size (unused) ++ audio_vchi_callback, // service callback ++ instance, // service callback parameter ++ VCOS_TRUE, //TODO: remove VCOS_FALSE, // unaligned bulk recieves ++ VCOS_TRUE, //TODO: remove VCOS_FALSE, // unaligned bulk transmits ++ VCOS_FALSE // want crc check on bulk transfers ++ }; ++ ++ status = vchi_service_open(vchi_instance, ¶ms, ++ &instance->vchi_handle[i]); ++ if (status != VCOS_SUCCESS) { ++ LOG_ERR ++ ("%s: failed to open VCHI service connection (status=%d)", ++ __func__, status); ++ ++ goto err_close_services; ++ } ++ /* Finished with the service for now */ ++ vchi_service_release(instance->vchi_handle[i]); ++ } ++ ++ return instance; ++ ++err_close_services: ++ for (i = 0; i < instance->num_connections; i++) { ++ vchi_service_close(instance->vchi_handle[i]); ++ } ++ ++ vcos_mutex_delete(&instance->vchi_mutex); ++ ++err_delete_event: ++ vcos_event_delete(&instance->msg_avail_event); ++ ++err_free_mem: ++ vcos_free(instance); ++ ++ return NULL; ++} ++ ++static int32_t vc_vchi_audio_deinit(AUDIO_INSTANCE_T * instance) ++{ ++ uint32_t i; ++ ++ LOG_DBG(" .. IN\n"); ++ ++ if (instance == NULL) { ++ LOG_ERR("%s: invalid handle %p", __func__, instance); ++ ++ return -1; ++ } ++ ++ LOG_DBG(" .. about to lock (%d)\n", instance->num_connections); ++ vcos_mutex_lock(&instance->vchi_mutex); ++ ++ /* Close all VCHI service connections */ ++ for (i = 0; i < instance->num_connections; i++) { ++ int32_t success; ++ LOG_DBG(" .. %i:closing %p\n", i, instance->vchi_handle[i]); ++ vchi_service_use(instance->vchi_handle[i]); ++ ++ success = vchi_service_close(instance->vchi_handle[i]); ++ if (success != 0) { ++ LOG_ERR ++ ("%s: failed to close VCHI service connection (status=%d)", ++ __func__, success); ++ } ++ } ++ ++ vcos_mutex_unlock(&instance->vchi_mutex); ++ ++ vcos_mutex_delete(&instance->vchi_mutex); ++ ++ vcos_event_delete(&instance->msg_avail_event); ++ ++ vcos_free(instance); ++ ++ /* Unregister the log category so we can add it back next time */ ++ vcos_log_unregister(&audio_log_category); ++ ++ LOG_DBG(" .. OUT\n"); ++ ++ return 0; ++} ++ ++static int bcm2835_audio_open_connection(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ static VCHI_INSTANCE_T vchi_instance; ++ static VCHI_CONNECTION_T *vchi_connection; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ LOG_INFO("%s: start", __func__); ++ //BUG_ON(instance); ++ if (instance) { ++ LOG_ERR("%s: VCHI instance already open (%p)", ++ __func__, instance); ++ instance->alsa_stream = alsa_stream; ++ alsa_stream->instance = instance; ++ ret = 0; // xxx todo -1; ++ goto err_free_mem; ++ } ++ ++ /* Initialize and create a VCHI connection */ ++ ret = vchi_initialise(&vchi_instance); ++ if (ret != 0) { ++ LOG_ERR("%s: failed to initialise VCHI instance (ret=%d)", ++ __func__, ret); ++ ++ ret = -EIO; ++ goto err_free_mem; ++ } ++ ret = vchi_connect(NULL, 0, vchi_instance); ++ if (ret != 0) { ++ LOG_ERR("%s: failed to connect VCHI instance (ret=%d)", ++ __func__, ret); ++ ++ ret = -EIO; ++ goto err_free_mem; ++ } ++ ++ /* Set up the VCOS logging */ ++ vcos_log_set_level(VCOS_LOG_CATEGORY, LOG_LEVEL); ++ vcos_log_register("audio", VCOS_LOG_CATEGORY); ++ ++ /* Initialize an instance of the audio service */ ++ instance = vc_vchi_audio_init(vchi_instance, &vchi_connection, 1); ++ ++ if (instance == NULL /*|| audio_handle != instance */ ) { ++ LOG_ERR("%s: failed to initialize audio service", __func__); ++ ++ ret = -EPERM; ++ goto err_free_mem; ++ } ++ ++ instance->alsa_stream = alsa_stream; ++ alsa_stream->instance = instance; ++ ++ LOG_DBG(" success !\n"); ++err_free_mem: ++ LOG_DBG(" .. OUT\n"); ++ ++ return ret; ++} ++ ++int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ AUDIO_INSTANCE_T *instance; ++ VC_AUDIO_MSG_T m; ++ int32_t success; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ my_workqueue_init(alsa_stream); ++ ++ ret = bcm2835_audio_open_connection(alsa_stream); ++ if (ret != 0) { ++ ret = -1; ++ goto exit; ++ } ++ instance = alsa_stream->instance; ++ ++ vcos_mutex_lock(&instance->vchi_mutex); ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ m.type = VC_AUDIO_MSG_TYPE_OPEN; ++ ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ vcos_mutex_unlock(&instance->vchi_mutex); ++exit: ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++static int bcm2835_audio_set_ctls_chan(bcm2835_alsa_stream_t * alsa_stream, ++ bcm2835_chip_t * chip) ++{ ++ VC_AUDIO_MSG_T m; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int32_t success; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ LOG_INFO ++ (" Setting ALSA dest(%d), volume(%d)\n", chip->dest, chip->volume); ++ ++ vcos_mutex_lock(&instance->vchi_mutex); ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ instance->got_result = 0; ++ instance->result = -1; ++ ++ m.type = VC_AUDIO_MSG_TYPE_CONTROL; ++ m.u.control.dest = chip->dest; ++ m.u.control.volume = chip->volume; ++ ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ /* We are expecting a reply from the videocore */ ++ while (!instance->got_result) { ++ success = vcos_event_wait(&instance->msg_avail_event); ++ if (success != VCOS_SUCCESS) { ++ LOG_ERR("%s: failed on waiting for event (status=%d)", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ } ++ ++ if (instance->result != 0) { ++ LOG_ERR("%s: result=%d", __func__, instance->result); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ vcos_mutex_unlock(&instance->vchi_mutex); ++ ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++int bcm2835_audio_set_ctls(bcm2835_chip_t * chip) ++{ ++ int i; ++ int ret = 0; ++ LOG_DBG(" .. IN\n"); ++ /* change ctls for all substreams */ ++ for (i = 0; i < MAX_SUBSTREAMS; i++) { ++ if (chip->avail_substreams & (1 << i)) { ++ if (!chip->alsa_stream[i]) ++ ret = 0; ++ else if (bcm2835_audio_set_ctls_chan ++ (chip->alsa_stream[i], chip) != 0) ++ ret = -1; ++ } ++ } ++ LOG_DBG(" .. OUT ret=%d\n", ret); ++ return ret; ++} ++ ++int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream, ++ uint32_t channels, uint32_t samplerate, ++ uint32_t bps) ++{ ++ VC_AUDIO_MSG_T m; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int32_t success; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ LOG_INFO ++ (" Setting ALSA channels(%d), samplerate(%d), bits-per-sample(%d)\n", ++ channels, samplerate, bps); ++ ++ /* resend ctls - alsa_stream may not have been open when first send */ ++ ret = bcm2835_audio_set_ctls_chan(alsa_stream, alsa_stream->chip); ++ if (ret != 0) { ++ LOG_ERR(" Alsa controls not supported\n"); ++ return -EINVAL; ++ } ++ ++ vcos_mutex_lock(&instance->vchi_mutex); ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ instance->got_result = 0; ++ instance->result = -1; ++ ++ m.type = VC_AUDIO_MSG_TYPE_CONFIG; ++ m.u.config.channels = channels; ++ m.u.config.samplerate = samplerate; ++ m.u.config.bps = bps; ++ ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ /* We are expecting a reply from the videocore */ ++ while (!instance->got_result) { ++ success = vcos_event_wait(&instance->msg_avail_event); ++ if (success != VCOS_SUCCESS) { ++ LOG_ERR("%s: failed on waiting for event (status=%d)", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ } ++ ++ if (instance->result != 0) { ++ LOG_ERR("%s: result=%d", __func__, instance->result); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ vcos_mutex_unlock(&instance->vchi_mutex); ++ ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++int bcm2835_audio_setup(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ LOG_DBG(" .. IN\n"); ++ ++ LOG_DBG(" .. OUT\n"); ++ ++ return 0; ++} ++ ++static int bcm2835_audio_start_worker(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ VC_AUDIO_MSG_T m; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int32_t success; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ vcos_mutex_lock(&instance->vchi_mutex); ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ m.type = VC_AUDIO_MSG_TYPE_START; ++ ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ vcos_mutex_unlock(&instance->vchi_mutex); ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++static int bcm2835_audio_stop_worker(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ VC_AUDIO_MSG_T m; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int32_t success; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ vcos_mutex_lock(&instance->vchi_mutex); ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ m.type = VC_AUDIO_MSG_TYPE_STOP; ++ m.u.stop.draining = alsa_stream->draining; ++ ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ vcos_mutex_unlock(&instance->vchi_mutex); ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ VC_AUDIO_MSG_T m; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int32_t success; ++ int ret; ++ LOG_DBG(" .. IN\n"); ++ ++ my_workqueue_quit(alsa_stream); ++ ++ vcos_mutex_lock(&instance->vchi_mutex); ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ m.type = VC_AUDIO_MSG_TYPE_CLOSE; ++ instance->got_result = 0; ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)", ++ __func__, success); ++ ret = -1; ++ goto unlock; ++ } ++ while (!instance->got_result) { ++ success = vcos_event_wait(&instance->msg_avail_event); ++ if (success != VCOS_SUCCESS) { ++ LOG_ERR("%s: failed on waiting for event (status=%d)", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ } ++ if (instance->result != 0) { ++ LOG_ERR("%s: failed result (status=%d)", ++ __func__, instance->result); ++ ++ ret = -1; ++ goto unlock; ++ } ++ ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ vcos_mutex_unlock(&instance->vchi_mutex); ++ ++ /* Stop the audio service */ ++ if (instance) { ++ vc_vchi_audio_deinit(instance); ++ alsa_stream->instance = NULL; ++ } ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++int bcm2835_audio_write(bcm2835_alsa_stream_t * alsa_stream, uint32_t count, ++ void *src) ++{ ++ VC_AUDIO_MSG_T m; ++ AUDIO_INSTANCE_T *instance = alsa_stream->instance; ++ int32_t success; ++ int ret; ++ ++ LOG_DBG(" .. IN\n"); ++ ++ LOG_INFO(" Writing %d bytes from %p\n", count, src); ++ ++ vcos_mutex_lock(&instance->vchi_mutex); ++ vchi_service_use(instance->vchi_handle[0]); ++ ++ m.type = VC_AUDIO_MSG_TYPE_WRITE; ++ m.u.write.count = count; ++ m.u.write.callback = alsa_stream->fifo_irq_handler; ++ m.u.write.cookie = alsa_stream; ++ m.u.write.silence = src == NULL; ++ ++ /* Send the message to the videocore */ ++ success = vchi_msg_queue(instance->vchi_handle[0], ++ &m, sizeof m, ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED, NULL); ++ ++ if (success != 0) { ++ LOG_ERR("%s: failed on vchi_msg_queue (status=%d)", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ LOG_DBG(" ... sent header\n"); ++ if (!m.u.write.silence) { ++ /* Send the message to the videocore */ ++ success = vchi_bulk_queue_transmit(instance->vchi_handle[0], ++ src, count, ++ 0 * ++ VCHI_FLAGS_BLOCK_UNTIL_QUEUED ++ + ++ 1 * ++ VCHI_FLAGS_BLOCK_UNTIL_DATA_READ, ++ NULL); ++ if (success != 0) { ++ LOG_ERR ++ ("%s: failed on vchi_bulk_queue_transmit (status=%d)", ++ __func__, success); ++ ++ ret = -1; ++ goto unlock; ++ } ++ } ++ ret = 0; ++ ++unlock: ++ vchi_service_release(instance->vchi_handle[0]); ++ vcos_mutex_unlock(&instance->vchi_mutex); ++ LOG_DBG(" .. OUT\n"); ++ return ret; ++} ++ ++/** ++ * Returns all buffers from arm->vc ++ */ ++void bcm2835_audio_flush_buffers(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ LOG_DBG(" .. IN\n"); ++ LOG_DBG(" .. OUT\n"); ++ return; ++} ++ ++/** ++ * Forces VC to flush(drop) its filled playback buffers and ++ * return them the us. (VC->ARM) ++ */ ++void bcm2835_audio_flush_playback_buffers(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ LOG_DBG(" .. IN\n"); ++ LOG_DBG(" .. OUT\n"); ++} ++ ++uint32_t bcm2835_audio_retrieve_buffers(bcm2835_alsa_stream_t * alsa_stream) ++{ ++ uint32_t count = atomic_read(&alsa_stream->retrieved); ++ atomic_sub(count, &alsa_stream->retrieved); ++ return count; ++} +--- /dev/null ++++ b/sound/arm/bcm2835.c +@@ -0,0 +1,424 @@ ++/***************************************************************************** ++* Copyright 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#include <linux/platform_device.h> ++ ++#include <linux/init.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++ ++#include "bcm2835.h" ++ ++/* module parameters (see "Module Parameters") */ ++/* SNDRV_CARDS: maximum number of cards supported by this module */ ++static int index[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = -1 }; ++static char *id[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = NULL }; ++static int enable[MAX_SUBSTREAMS] = {[0 ... (MAX_SUBSTREAMS - 1)] = 1 }; ++ ++/* HACKY global pointers needed for successive probes to work : ssp ++ * But compared against the changes we will have to do in VC audio_ipc code ++ * to export 8 audio_ipc devices as a single IPC device and then monitor all ++ * four devices in a thread, this gets things done quickly and should be easier ++ * to debug if we run into issues ++ */ ++ ++static struct snd_card *g_card = NULL; ++static bcm2835_chip_t *g_chip = NULL; ++ ++static int snd_bcm2835_free(bcm2835_chip_t * chip) ++{ ++ kfree(chip); ++ return 0; ++} ++ ++/* component-destructor ++ * (see "Management of Cards and Components") ++ */ ++static int snd_bcm2835_dev_free(struct snd_device *device) ++{ ++ return snd_bcm2835_free(device->device_data); ++} ++ ++/* chip-specific constructor ++ * (see "Management of Cards and Components") ++ */ ++static int __devinit snd_bcm2835_create(struct snd_card *card, ++ struct platform_device *pdev, ++ bcm2835_chip_t ** rchip) ++{ ++ bcm2835_chip_t *chip; ++ int err; ++ static struct snd_device_ops ops = { ++ .dev_free = snd_bcm2835_dev_free, ++ }; ++ ++ *rchip = NULL; ++ ++ chip = kzalloc(sizeof(*chip), GFP_KERNEL); ++ if (chip == NULL) ++ return -ENOMEM; ++ ++ chip->card = card; ++ ++ err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops); ++ if (err < 0) { ++ snd_bcm2835_free(chip); ++ return err; ++ } ++ ++ *rchip = chip; ++ return 0; ++} ++ ++static int __devinit snd_bcm2835_alsa_probe(struct platform_device *pdev) ++{ ++ static int dev; ++ bcm2835_chip_t *chip; ++ struct snd_card *card; ++ int err; ++ printk(KERN_INFO "### snd_bcm2835_alsa_probe %p ###", pdev); ++ ++ printk ++ ("############ PROBING FOR bcm2835 ALSA device (%d):(%d) ###############\n", ++ dev, enable[dev]); ++ ++ if (dev >= MAX_SUBSTREAMS) ++ return -ENODEV; ++ ++ if (!enable[dev]) { ++ dev++; ++ return -ENOENT; ++ } ++ ++ if (dev > 0) ++ goto add_register_map; ++ ++ printk("Creating card...\n"); ++ err = snd_card_create(index[dev], id[dev], THIS_MODULE, 0, &g_card); ++ if (err < 0) ++ goto out; ++ ++ snd_card_set_dev(g_card, &pdev->dev); ++ strcpy(g_card->driver, "BRCM bcm2835 ALSA Driver"); ++ strcpy(g_card->shortname, "bcm2835 ALSA"); ++ sprintf(g_card->longname, "%s", g_card->shortname); ++ ++ printk("Creating device/chip ..\n"); ++ err = snd_bcm2835_create(g_card, pdev, &chip); ++ if (err < 0) { ++ printk(KERN_ERR "Failed to create bcm2835 chip\n"); ++ goto out_bcm2835_create; ++ } ++ ++ g_chip = chip; ++ err = snd_bcm2835_new_pcm(chip); ++ if (err < 0) { ++ printk(KERN_ERR "Failed to create new BCM2835 pcm device\n"); ++ goto out_bcm2835_new_pcm; ++ } ++ ++ printk("Adding controls ..\n"); ++ err = snd_bcm2835_new_ctl(chip); ++ if (err < 0) { ++ printk(KERN_ERR "Failed to create new BCM2835 ctl\n"); ++ goto out_bcm2835_new_ctl; ++ } ++ ++add_register_map: ++ card = g_card; ++ chip = g_chip; ++ ++ BUG_ON(!(card && chip)); ++ ++ chip->avail_substreams |= (1 << dev); ++ chip->pdev[dev] = pdev; ++ ++ if (dev == 0) { ++ printk("Registering card ....\n"); ++ err = snd_card_register(card); ++ if (err < 0) { ++ printk(KERN_ERR ++ "Failed to register bcm2835 ALSA card \n"); ++ goto out_card_register; ++ } ++ platform_set_drvdata(pdev, card); ++ printk("bcm2835 ALSA CARD CREATED!\n"); ++ } else { ++ printk("bcm2835 ALSA CHIP CREATED!\n"); ++ platform_set_drvdata(pdev, (void *)dev); ++ } ++ ++ dev++; ++ ++ return 0; ++ ++out_card_register: ++out_bcm2835_new_ctl: ++out_bcm2835_new_pcm: ++out_bcm2835_create: ++ BUG_ON(!g_card); ++ if (snd_card_free(g_card)) ++ printk(KERN_ERR "Failed to free Registered alsa card\n"); ++ g_card = NULL; ++out: ++ dev = SNDRV_CARDS; /* stop more avail_substreams from being probed */ ++ printk(KERN_ERR "BCM2835 ALSA Probe failed !!\n"); ++ return err; ++} ++ ++static int snd_bcm2835_alsa_remove(struct platform_device *pdev) ++{ ++ uint32_t idx; ++ void *drv_data; ++ ++ drv_data = platform_get_drvdata(pdev); ++ ++ if (drv_data == (void *)g_card) { ++ /* This is the card device */ ++ snd_card_free((struct snd_card *)drv_data); ++ g_card = NULL; ++ g_chip = NULL; ++ } else { ++ idx = (uint32_t) drv_data; ++ if (g_card != NULL) { ++ BUG_ON(!g_chip); ++ /* We pass chip device numbers in audio ipc devices ++ * other than the one we registered our card with ++ */ ++ idx = (uint32_t) drv_data; ++ BUG_ON(!idx || idx > MAX_SUBSTREAMS); ++ g_chip->avail_substreams &= ~(1 << idx); ++ /* There should be atleast one substream registered ++ * after we are done here, as it wil be removed when ++ * the *remove* is called for the card device ++ */ ++ BUG_ON(!g_chip->avail_substreams); ++ } ++ } ++ ++ platform_set_drvdata(pdev, NULL); ++ ++ return 0; ++} ++ ++#ifdef CONFIG_PM ++static int snd_bcm2835_alsa_suspend(struct platform_device *pdev, ++ pm_message_t state) ++{ ++ return 0; ++} ++ ++static int snd_bcm2835_alsa_resume(struct platform_device *pdev) ++{ ++ return 0; ++} ++ ++#endif ++ ++static struct platform_driver bcm2835_alsa0_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD0", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa1_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD1", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa2_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD2", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa3_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD3", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa4_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD4", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa5_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD5", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa6_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD6", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static struct platform_driver bcm2835_alsa7_driver = { ++ .probe = snd_bcm2835_alsa_probe, ++ .remove = snd_bcm2835_alsa_remove, ++#ifdef CONFIG_PM ++ .suspend = snd_bcm2835_alsa_suspend, ++ .resume = snd_bcm2835_alsa_resume, ++#endif ++ .driver = { ++ .name = "bcm2835_AUD7", ++ .owner = THIS_MODULE, ++ }, ++}; ++ ++static int __devinit bcm2835_alsa_device_init(void) ++{ ++ int err; ++ err = platform_driver_register(&bcm2835_alsa0_driver); ++ if (err) { ++ printk("Error registering bcm2835_alsa0_driver %d .\n", err); ++ goto out; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa1_driver); ++ if (err) { ++ printk("Error registering bcm2835_alsa1_driver %d .\n", err); ++ goto unregister_0; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa2_driver); ++ if (err) { ++ printk("Error registering bcm2835_alsa2_driver %d .\n", err); ++ goto unregister_1; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa3_driver); ++ if (err) { ++ printk("Error registering bcm2835_alsa3_driver %d .\n", err); ++ goto unregister_2; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa4_driver); ++ if (err) { ++ printk("Error registering bcm2835_alsa4_driver %d .\n", err); ++ goto unregister_3; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa5_driver); ++ if (err) { ++ printk("Error registering bcm2835_alsa5_driver %d .\n", err); ++ goto unregister_4; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa6_driver); ++ if (err) { ++ printk("Error registering bcm2835_alsa6_driver %d .\n", err); ++ goto unregister_5; ++ } ++ ++ err = platform_driver_register(&bcm2835_alsa7_driver); ++ if (err) { ++ printk("Error registering bcm2835_alsa7_driver %d .\n", err); ++ goto unregister_6; ++ } ++ printk(KERN_INFO "### BCM2835 ALSA driver init %s ### \n", ++ err ? "FAILED" : "OK"); ++ ++ return 0; ++ ++unregister_6: ++ platform_driver_unregister(&bcm2835_alsa6_driver); ++unregister_5: ++ platform_driver_unregister(&bcm2835_alsa5_driver); ++unregister_4: ++ platform_driver_unregister(&bcm2835_alsa4_driver); ++unregister_3: ++ platform_driver_unregister(&bcm2835_alsa3_driver); ++unregister_2: ++ platform_driver_unregister(&bcm2835_alsa2_driver); ++unregister_1: ++ platform_driver_unregister(&bcm2835_alsa1_driver); ++unregister_0: ++ platform_driver_unregister(&bcm2835_alsa0_driver); ++out: ++ return err; ++} ++ ++static void __devexit bcm2835_alsa_device_exit(void) ++{ ++ platform_driver_unregister(&bcm2835_alsa0_driver); ++ platform_driver_unregister(&bcm2835_alsa1_driver); ++ platform_driver_unregister(&bcm2835_alsa2_driver); ++ platform_driver_unregister(&bcm2835_alsa3_driver); ++ platform_driver_unregister(&bcm2835_alsa4_driver); ++ platform_driver_unregister(&bcm2835_alsa5_driver); ++ platform_driver_unregister(&bcm2835_alsa6_driver); ++ platform_driver_unregister(&bcm2835_alsa7_driver); ++} ++ ++late_initcall(bcm2835_alsa_device_init); ++module_exit(bcm2835_alsa_device_exit); ++ ++MODULE_AUTHOR("Dom Cobley"); ++MODULE_DESCRIPTION("Alsa driver for BCM2835 chip"); ++MODULE_LICENSE("GPL"); ++MODULE_ALIAS("platform:bcm2835_alsa"); +--- /dev/null ++++ b/sound/arm/bcm2835.h +@@ -0,0 +1,242 @@ ++/***************************************************************************** ++* Copyright 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#ifndef __SOUND_ARM_BCM2835_H ++#define __SOUND_ARM_BCM2835_H ++ ++#define SUBSTREAM_NUM 1 ++ ++#include <linux/device.h> ++#include <linux/list.h> ++#include <linux/interrupt.h> ++#include <linux/wait.h> ++#include <sound/core.h> ++#include <sound/initval.h> ++#include <sound/pcm.h> ++#include <sound/pcm_params.h> ++#include <linux/workqueue.h> ++ ++/* #define DUMP_RAW_DATA */ ++//#define AUDIO_DEBUG_ENABLE ++//#define AUDIO_VERBOSE_DEBUG_ENABLE ++ ++/* Debug macros */ ++#ifdef AUDIO_DEBUG_ENABLE ++ ++#ifdef AUDIO_VERBOSE_DEBUG_ENABLE ++ ++#define audio_debug(fmt, arg...) \ ++ printk(KERN_INFO"%s:%d " fmt, __func__, __LINE__, ##arg) ++ ++#define audio_info(fmt, arg...) \ ++ printk(KERN_INFO"%s:%d " fmt, __func__, __LINE__, ##arg) ++ ++#else ++ ++#define audio_debug(fmt, arg...) do {} while (0) ++ ++#define audio_info(fmt, arg...) do {} while (0) ++ ++#endif /* AUDIO_VERBOSE_DEBUG_ENABLE */ ++ ++#else ++ ++#define audio_debug(fmt, arg...) do {} while (0) ++ ++#define audio_info(fmt, arg...) do {} while (0) ++ ++#endif /* AUDIO_DEBUG_ENABLE */ ++ ++#define audio_error(fmt, arg...) \ ++ printk(KERN_ERR"%s:%d " fmt, __func__, __LINE__, ##arg) ++ ++#define audio_warning(fmt, arg...) \ ++ printk(KERN_WARNING"%s:%d " fmt, __func__, __LINE__, ##arg) ++ ++#define audio_alert(fmt, arg...) \ ++ printk(KERN_ALERT"%s:%d " fmt, __func__, __LINE__, ##arg) ++ ++#define MAX_SUBSTREAMS (8) ++#define AVAIL_SUBSTREAMS_MASK (0xff) ++ ++#define AUDIO_IPC_BLOCK_NUM_BUFFERS (8) ++#define AUDIO_IPC_BLOCK_BUFFER_SIZE (1024*8) ++ ++#define AUDIO_CONTROL_OFFSET (0x00) ++#define CTRL_EN_SHIFT (0) ++#define CTRL_EN_MASK (0x00000001) ++#define CTRL_PLAY_SHIFT (1) ++#define CTRL_PLAY_MASK (0x00000002) ++#define CTRL_MUTE_SHIFT (2) ++#define CTRL_MUTE_MASK (0x00000004) ++#define CTRL_SETUP_SHIFT (3) ++#define CTRL_SETUP_MASK (0x00000008) ++#define CTRL_FLUSH_SHIFT (4) ++#define CTRL_FLUSH_MASK (0x00000010) ++#define CTRL_STOPMODE_SHIFT (5) ++#define CTRL_STOPMODE_MASK (0x00000020) ++ ++#define AUDIO_STATUS_OFFSET (0x04) ++#define STAT_EN_SHIFT (0) ++#define STAT_EN_MASK (0x00000001) ++#define STAT_PLAY_SHIFT (1) ++#define STAT_PLAY_MASK (0x00000002) ++#define STAT_MUTE_SHIFT (2) ++#define STAT_MUTE_MASK (0x00000004) ++#define STAT_SETUP_SHIFT (3) ++#define STAT_SETUP_MASK (0x00000008) ++#define STAT_FLUSH_SHIFT (4) ++#define STAT_FLUSH_MASK (0x00000010) ++#define STAT_STOPMODE_SHIFT (5) ++#define STAT_STOPMODE_MASK (0x00000020) ++ ++/* Interrupt status */ ++#define AUDIO_INTSTAT_OFFSET (0x08) ++#define INTSTAT_CONTROL_SHIFT (0) ++#define INTSTAT_CONTROL_MASK (0x0000000f) ++#define INTSTAT_FIFO_SHIFT (4) ++#define INTSTAT_FIFO_MASK (0x000000f0) ++ ++/* Configuration */ ++#define AUDIO_DESTINATION_OFFSET (0x0C) ++#define AUDIO_SAMPLE_RATE_OFFSET (0x10) ++#define AUDIO_BIT_RATE_OFFSET (0x14) ++#define AUDIO_VOLUME_OFFSET (0x18) ++#define AUDIO_CHANNELS_OFFSET (0x1C) ++ ++/* Implemention of peterson's algorithm for shared memory semaphores */ ++#define AUDIO_FLAG0_OFFSET (0x20) ++#define AUDIO_FLAG1_OFFSET (0x24) ++#define AUDIO_TURN_OFFSET (0x28) ++ ++/* Fifo registers */ ++#define AUDIO_IN_WRITE_PTR_OFFSET (0x30) ++#define AUDIO_IN_READ_PTR_OFFSET (0x34) ++#define AUDIO_IN_FIFO_SIZE_OFFSET (0x38) ++#define AUDIO_IN_FIFO_ENTRY_OFFSET (0x3C) ++#define AUDIO_IN_FIFO_START_OFFSET (0x40) ++ ++/* 8 entries here of 4 words each = 0x80 gap from 0x50 */ ++#define AUDIO_IN_FIFO_OFFSET (0x50) ++ ++#define AUDIO_OUT_WRITE_PTR_OFFSET (0xD0) ++#define AUDIO_OUT_READ_PTR_OFFSET (0xD4) ++#define AUDIO_OUT_FIFO_SIZE_OFFSET (0xD8) ++#define AUDIO_OUT_FIFO_ENTRY_OFFSET (0xDC) ++#define AUDIO_OUT_FIFO_START_OFFSET (0xE0) ++ ++/* 8 entries here of 4 words each = 0x80 gap from 0xF0 */ ++#define AUDIO_OUT_FIFO_OFFSET (0xF0) ++ ++/* Some constants for values .. */ ++typedef enum { ++ AUDIO_DEST_AUTO = 0, ++ AUDIO_DEST_HEADPHONES = 1, ++ AUDIO_DEST_HDMI = 2, ++ AUDIO_DEST_MAX, ++} SND_BCM2835_ROUTE_T; ++ ++typedef enum { ++ PCM_PLAYBACK_VOLUME, ++ PCM_PLAYBACK_MUTE, ++ PCM_PLAYBACK_DEVICE, ++} SND_BCM2835_CTRL_T; ++ ++/* this struct is tightly packed - its size is 16bytes */ ++typedef struct { ++ uint32_t buffer_id; ++ uint32_t buffer_size; ++ uint32_t buffer_ptr; ++ uint32_t spare; ++ ++} AUDIO_FIFO_ENTRY_T; ++ ++/* definition of the chip-specific record */ ++typedef struct bcm2835_chip { ++ struct snd_card *card; ++ struct snd_pcm *pcm; ++ /* Bitmat for valid reg_base and irq numbers */ ++ uint32_t avail_substreams; ++ struct platform_device *pdev[MAX_SUBSTREAMS]; ++ struct bcm2835_alsa_stream *alsa_stream[MAX_SUBSTREAMS]; ++ ++ int volume; ++ int dest; ++ int mute; ++} bcm2835_chip_t; ++ ++typedef struct bcm2835_audio_buffer { ++ uint32_t buffer_id; ++ phys_addr_t bus_addr; ++ uint8_t __iomem *start; ++ uint32_t size; ++ uint32_t data_left; ++ struct list_head link; ++ ++} bcm2835_audio_buffer_t; ++ ++typedef struct bcm2835_alsa_stream { ++ bcm2835_chip_t *chip; ++ struct snd_pcm_substream *substream; ++ ++ struct semaphore buffers_update_sem; ++ struct semaphore control_sem; ++ spinlock_t lock; ++ volatile uint32_t control; ++ volatile uint32_t status; ++ ++ int open; ++ int running; ++ int draining; ++ ++#ifdef DUMP_RAW_DATA ++ /* for debug */ ++ int file; ++#endif ++ unsigned int pos; ++ unsigned int buffer_size; ++ unsigned int period_size; ++ ++ uint32_t enable_fifo_irq; ++ irq_handler_t fifo_irq_handler; ++ ++ atomic_t retrieved; ++ struct opaque_AUDIO_INSTANCE_T *instance; ++ struct workqueue_struct *my_wq; ++ int idx; ++} bcm2835_alsa_stream_t; ++ ++int snd_bcm2835_new_ctl(bcm2835_chip_t * chip); ++int snd_bcm2835_new_pcm(bcm2835_chip_t * chip); ++ ++void bcm2835_audio_fifo_get_lock(bcm2835_alsa_stream_t * alsa_stream); ++void bcm2835_audio_fifo_put_lock(bcm2835_alsa_stream_t * alsa_stream); ++ ++int bcm2835_audio_open(bcm2835_alsa_stream_t * alsa_stream); ++int bcm2835_audio_close(bcm2835_alsa_stream_t * alsa_stream); ++int bcm2835_audio_set_params(bcm2835_alsa_stream_t * alsa_stream, ++ uint32_t channels, uint32_t samplerate, ++ uint32_t bps); ++int bcm2835_audio_setup(bcm2835_alsa_stream_t * alsa_stream); ++int bcm2835_audio_start(bcm2835_alsa_stream_t * alsa_stream); ++int bcm2835_audio_stop(bcm2835_alsa_stream_t * alsa_stream); ++int bcm2835_audio_set_ctls(bcm2835_chip_t * chip); ++int bcm2835_audio_write(bcm2835_alsa_stream_t * alsa_stream, uint32_t count, ++ void *src); ++//uint32_t bcm2835_audio_buffers_consumed_bytes(bcm2835_alsa_stream_t *alsa_stream); ++uint32_t bcm2835_audio_retrieve_buffers(bcm2835_alsa_stream_t * alsa_stream); ++void bcm2835_audio_flush_buffers(bcm2835_alsa_stream_t * alsa_stream); ++void bcm2835_audio_flush_playback_buffers(bcm2835_alsa_stream_t * alsa_stream); ++ ++#endif /* __SOUND_ARM_BCM2835_H */ +--- /dev/null ++++ b/sound/arm/vc_vchi_audioserv_defs.h +@@ -0,0 +1,112 @@ ++/***************************************************************************** ++* Copyright 2011 Broadcom Corporation. All rights reserved. ++* ++* Unless you and Broadcom execute a separate written software license ++* agreement governing use of this software, this software is licensed to you ++* under the terms of the GNU General Public License version 2, available at ++* http://www.broadcom.com/licenses/GPLv2.php (the "GPL"). ++* ++* Notwithstanding the above, under no circumstances may you combine this ++* software in any way with any other Broadcom software provided under a ++* license other than the GPL, without Broadcom's express prior written ++* consent. ++*****************************************************************************/ ++ ++#ifndef _VC_AUDIO_DEFS_H_ ++#define _VC_AUDIO_DEFS_H_ ++ ++// FourCC code used for VCHI connection ++#define VC_AUDIO_SERVER_NAME MAKE_FOURCC("AUDS") ++ ++// Maximum message length ++#define VC_AUDIO_MAX_MSG_LEN (sizeof( VC_AUDIO_MSG_T )) ++ ++// List of screens that are currently supported ++// All message types supported for HOST->VC direction ++typedef enum { ++ VC_AUDIO_MSG_TYPE_RESULT, // Generic result ++ VC_AUDIO_MSG_TYPE_COMPLETE, // Generic result ++ VC_AUDIO_MSG_TYPE_CONFIG, // Configure audio ++ VC_AUDIO_MSG_TYPE_CONTROL, // Configure audio ++ VC_AUDIO_MSG_TYPE_OPEN, // Configure audio ++ VC_AUDIO_MSG_TYPE_CLOSE, // Configure audio ++ VC_AUDIO_MSG_TYPE_START, // Configure audio ++ VC_AUDIO_MSG_TYPE_STOP, // Configure audio ++ VC_AUDIO_MSG_TYPE_WRITE, // Configure audio ++ VC_AUDIO_MSG_TYPE_MAX ++} VC_AUDIO_MSG_TYPE; ++ ++// configure the audio ++typedef struct { ++ uint32_t channels; ++ uint32_t samplerate; ++ uint32_t bps; ++ ++} VC_AUDIO_CONFIG_T; ++ ++typedef struct { ++ uint32_t volume; ++ uint32_t dest; ++ ++} VC_AUDIO_CONTROL_T; ++ ++// audio ++typedef struct { ++ uint32_t dummy; ++ ++} VC_AUDIO_OPEN_T; ++ ++// audio ++typedef struct { ++ uint32_t dummy; ++ ++} VC_AUDIO_CLOSE_T; ++// audio ++typedef struct { ++ uint32_t dummy; ++ ++} VC_AUDIO_START_T; ++// audio ++typedef struct { ++ uint32_t draining; ++ ++} VC_AUDIO_STOP_T; ++ ++// configure the write audio samples ++typedef struct { ++ uint32_t count; // in bytes ++ void *callback; ++ void *cookie; ++ uint32_t silence; ++} VC_AUDIO_WRITE_T; ++ ++// Generic result for a request (VC->HOST) ++typedef struct { ++ int32_t success; // Success value ++ ++} VC_AUDIO_RESULT_T; ++ ++// Generic result for a request (VC->HOST) ++typedef struct { ++ int32_t count; // Success value ++ void *callback; ++ void *cookie; ++} VC_AUDIO_COMPLETE_T; ++ ++// Message header for all messages in HOST->VC direction ++typedef struct { ++ int32_t type; // Message type (VC_AUDIO_MSG_TYPE) ++ union { ++ VC_AUDIO_CONFIG_T config; ++ VC_AUDIO_CONTROL_T control; ++ VC_AUDIO_OPEN_T open; ++ VC_AUDIO_CLOSE_T close; ++ VC_AUDIO_START_T start; ++ VC_AUDIO_STOP_T stop; ++ VC_AUDIO_WRITE_T write; ++ VC_AUDIO_RESULT_T result; ++ VC_AUDIO_COMPLETE_T complete; ++ } u; ++} VC_AUDIO_MSG_T; ++ ++#endif // _VC_AUDIO_DEFS_H_ diff --git a/target/linux/brcm2708/patches-3.3/0510-fix_divdi3.patch b/target/linux/brcm2708/patches-3.3/0510-fix_divdi3.patch new file mode 100644 index 0000000000..82b6e067c6 --- /dev/null +++ b/target/linux/brcm2708/patches-3.3/0510-fix_divdi3.patch @@ -0,0 +1,11 @@ +--- a/arch/arm/lib/Makefile ++++ b/arch/arm/lib/Makefile +@@ -14,7 +14,7 @@ lib-y := backtrace.o changebit.o csumip + ashldi3.o ashrdi3.o lshrdi3.o muldi3.o \ + ucmpdi2.o lib1funcs.o div64.o \ + io-readsb.o io-writesb.o io-readsl.o io-writesl.o \ +- call_with_stack.o ++ call_with_stack.o divdi3.o + + mmu-y := clear_user.o copy_page.o getuser.o putuser.o + |