mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-28 12:34:26 -04:00 
			
		
		
		
	Add support for Raspberry Pi / brcm2708 / 2835 Signed-off-by: Ian Ridge <ianridge [at] gmail.com> SVN-Revision: 32825
		
			
				
	
	
		
			43254 lines
		
	
	
		
			1.3 MiB
		
	
	
	
	
	
	
	
			
		
		
	
	
			43254 lines
		
	
	
		
			1.3 MiB
		
	
	
	
	
	
	
	
| 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;
 |