Char/Misc driver patches for 3.14-rc1

Here's the big char/misc driver patches for 3.14-rc1.
 
 Lots of little things, and a new "big" driver, genwqe.  Full details are
 in the shortlog.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v2.0.22 (GNU/Linux)
 
 iEYEABECAAYFAlLdgc8ACgkQMUfUDdst+ynt9ACguAhrO2WlowdcUZELadTEpqLX
 gbkAn0+c1rjizVdIRvs83aQRNJP6w1uR
 =oPmD
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver patches from Greg KH:
 "Here's the big char/misc driver patches for 3.14-rc1.

  Lots of little things, and a new "big" driver, genwqe.  Full details
  are in the shortlog"

* tag 'char-misc-3.14-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc: (90 commits)
  mei: limit the number of consecutive resets
  mei: revamp mei reset state machine
  drivers/char: don't use module_init in non-modular ttyprintk.c
  VMCI: fix error handling path when registering guest driver
  extcon: gpio: Add power resume support
  Documentation: HOWTO: Updates on subsystem trees, patchwork, -next (vs. -mm) in ko_KR
  Documentation: HOWTO: update for 2.6.x -> 3.x versioning in ko_KR
  Documentation: HOWTO: update stable address in ko_KR
  Documentation: HOWTO: update LXR web link in ko_KR
  char: nwbutton: open-code interruptible_sleep_on
  mei: fix syntax in comments and debug output
  mei: nfc: mei_nfc_free has to be called under lock
  mei: use hbm idle state to prevent spurious resets
  mei: do not run reset flow from the interrupt thread
  misc: genwqe: fix return value check in genwqe_device_create()
  GenWQE: Fix warnings for sparc
  GenWQE: Fix compile problems for Alpha
  Documentation/misc-devices/mei/mei-amt-version.c: remove unneeded call of mei_deinit()
  GenWQE: Rework return code for flash-update ioctl
  sgi-xp: open-code interruptible_sleep_on_timeout
  ...
This commit is contained in:
Linus Torvalds 2014-01-20 15:48:19 -08:00
commit 9f67627a0f
82 changed files with 9185 additions and 690 deletions

View File

@ -0,0 +1,91 @@
What: /sys/kernel/debug/genwqe/genwqe<n>_card/ddcb_info
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: DDCB queue dump used for debugging queueing problems.
What: /sys/kernel/debug/genwqe/genwqe<n>_card/curr_regs
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Dump of the current error registers.
Only available for PF.
What: /sys/kernel/debug/genwqe/genwqe<n>_card/curr_dbg_uid0
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Internal chip state of UID0 (unit id 0).
Only available for PF.
What: /sys/kernel/debug/genwqe/genwqe<n>_card/curr_dbg_uid1
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Internal chip state of UID1.
Only available for PF.
What: /sys/kernel/debug/genwqe/genwqe<n>_card/curr_dbg_uid2
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Internal chip state of UID2.
Only available for PF.
What: /sys/kernel/debug/genwqe/genwqe<n>_card/prev_regs
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Dump of the error registers before the last reset of
the card occured.
Only available for PF.
What: /sys/kernel/debug/genwqe/genwqe<n>_card/prev_dbg_uid0
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Internal chip state of UID0 before card was reset.
Only available for PF.
What: /sys/kernel/debug/genwqe/genwqe<n>_card/prev_dbg_uid1
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Internal chip state of UID1 before card was reset.
Only available for PF.
What: /sys/kernel/debug/genwqe/genwqe<n>_card/prev_dbg_uid2
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Internal chip state of UID2 before card was reset.
Only available for PF.
What: /sys/kernel/debug/genwqe/genwqe<n>_card/info
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Comprehensive summary of bitstream version and software
version. Used bitstream and bitstream clocking information.
What: /sys/kernel/debug/genwqe/genwqe<n>_card/err_inject
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Possibility to inject error cases to ensure that the drivers
error handling code works well.
What: /sys/kernel/debug/genwqe/genwqe<n>_card/vf<0..14>_jobtimeout_msec
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Default VF timeout 250ms. Testing might require 1000ms.
Using 0 will use the cards default value (whatever that is).
The timeout depends on the max number of available cards
in the system and the maximum allowed queue size.
The driver ensures that the settings are done just before
the VFs get enabled. Changing the timeouts in flight is not
possible.
Only available for PF.
What: /sys/kernel/debug/genwqe/genwqe<n>_card/jobtimer
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Dump job timeout register values for PF and VFs.
Only available for PF.
What: /sys/kernel/debug/genwqe/genwqe<n>_card/queue_working_time
Date: Dec 2013
Contact: haver@linux.vnet.ibm.com
Description: Dump queue working time register values for PF and VFs.
Only available for PF.

View File

@ -0,0 +1,62 @@
What: /sys/class/genwqe/genwqe<n>_card/version
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Unique bitstream identification e.g.
'0000000330336283.00000000475a4950'.
What: /sys/class/genwqe/genwqe<n>_card/appid
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Identifies the currently active card application e.g. 'GZIP'
for compression/decompression.
What: /sys/class/genwqe/genwqe<n>_card/type
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Type of the card e.g. 'GenWQE5-A7'.
What: /sys/class/genwqe/genwqe<n>_card/curr_bitstream
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Currently active bitstream. 1 is default, 0 is backup.
What: /sys/class/genwqe/genwqe<n>_card/next_bitstream
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Interface to set the next bitstream to be used.
What: /sys/class/genwqe/genwqe<n>_card/tempsens
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Interface to read the cards temperature sense register.
What: /sys/class/genwqe/genwqe<n>_card/freerunning_timer
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Interface to read the cards free running timer.
Used for performance and utilization measurements.
What: /sys/class/genwqe/genwqe<n>_card/queue_working_time
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Interface to read queue working time.
Used for performance and utilization measurements.
What: /sys/class/genwqe/genwqe<n>_card/state
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: State of the card: "unused", "used", "error".
What: /sys/class/genwqe/genwqe<n>_card/base_clock
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Base clock frequency of the card.
What: /sys/class/genwqe/genwqe<n>_card/device/sriov_numvfs
Date: Oct 2013
Contact: haver@linux.vnet.ibm.com
Description: Enable VFs (1..15):
sudo sh -c 'echo 15 > \
/sys/bus/pci/devices/0000\:1b\:00.0/sriov_numvfs'
Disable VFs:
Write a 0 into the same sysfs entry.

View File

@ -112,7 +112,7 @@ required reading:
Other excellent descriptions of how to create patches properly are:
"The Perfect Patch"
http://kerneltrap.org/node/3737
http://www.ozlabs.org/~akpm/stuff/tpp.txt
"Linux kernel patch submission format"
http://linux.yyz.us/patch-format.html
@ -579,7 +579,7 @@ all time. It should describe the patch completely, containing:
For more details on what this should all look like, please see the
ChangeLog section of the document:
"The Perfect Patch"
http://userweb.kernel.org/~akpm/stuff/tpp.txt
http://www.ozlabs.org/~akpm/stuff/tpp.txt

View File

@ -20,6 +20,10 @@ TC/TCLIB Timer required properties:
- interrupts: Should contain all interrupts for the TC block
Note that you can specify several interrupt cells if the TC
block has one interrupt per channel.
- clock-names: tuple listing input clock names.
Required elements: "t0_clk"
Optional elements: "t1_clk", "t2_clk"
- clocks: phandles to input clocks.
Examples:
@ -28,6 +32,8 @@ One interrupt per TC block:
compatible = "atmel,at91rm9200-tcb";
reg = <0xfff7c000 0x100>;
interrupts = <18 4>;
clocks = <&tcb0_clk>;
clock-names = "t0_clk";
};
One interrupt per TC channel in a TC block:
@ -35,6 +41,8 @@ One interrupt per TC channel in a TC block:
compatible = "atmel,at91rm9200-tcb";
reg = <0xfffdc000 0x100>;
interrupts = <26 4 27 4 28 4>;
clocks = <&tcb1_clk>;
clock-names = "t0_clk";
};
RSTC Reset Controller required properties:

View File

@ -2,7 +2,11 @@ EXTCON FOR PALMAS/TWL CHIPS
PALMAS USB COMPARATOR
Required Properties:
- compatible : Should be "ti,palmas-usb" or "ti,twl6035-usb"
- compatible: should contain one of:
* "ti,palmas-usb-vid".
* "ti,twl6035-usb-vid".
* "ti,palmas-usb" (DEPRECATED - use "ti,palmas-usb-vid").
* "ti,twl6035-usb" (DEPRECATED - use "ti,twl6035-usb-vid").
Optional Properties:
- ti,wakeup : To enable the wakeup comparator in probe

View File

@ -6,6 +6,9 @@ Required properties:
- atmel,at91sam9g45-ssc: support dma transfer
- reg: Should contain SSC registers location and length
- interrupts: Should contain SSC interrupt
- clock-names: tuple listing input clock names.
Required elements: "pclk"
- clocks: phandles to input clocks.
Required properties for devices compatible with "atmel,at91sam9g45-ssc":
@ -20,6 +23,8 @@ ssc0: ssc@fffbc000 {
compatible = "atmel,at91rm9200-ssc";
reg = <0xfffbc000 0x4000>;
interrupts = <14 4 5>;
clocks = <&ssc0_clk>;
clock-names = "pclk";
};
- DMA transfer:

View File

@ -8,6 +8,8 @@ Optional properties:
- temp-measurement-period: temperature measurement period (milliseconds)
- default-oversampling: default oversampling value to be used at startup,
value range is 0-3 with rising sensitivity.
- interrupt-parent: should be the phandle for the interrupt controller
- interrupts: interrupt mapping for IRQ
Example:
@ -17,4 +19,6 @@ pressure@77 {
chip-id = <10>;
temp-measurement-period = <100>;
default-oversampling = <2>;
interrupt-parent = <&gpio0>;
interrupts = <25 IRQ_TYPE_EDGE_RISING>;
};

Before

Width:  |  Height:  |  Size: 519 B

After

Width:  |  Height:  |  Size: 701 B

View File

@ -50,7 +50,7 @@ so that they are still compatible with legacy userspace processes.
Extcon's extended features for switch device drivers with
complex features usually required magic numbers in state
value of switch_dev. With extcon, such magic numbers that
support multiple cables (
support multiple cables are no more required or supported.
1. Define cable names at edev->supported_cable.
2. (Recommended) remove print_state callback.
@ -114,11 +114,8 @@ exclusive, the two cables cannot be in ATTACHED state simulteneously.
****** ABI Location
If "CONFIG_ANDROID" is enabled and "CONFIG_ANDROID_SWITCH" is
disabled, /sys/class/switch/* are created as symbolic links to
/sys/class/extcon/*. Because CONFIG_ANDROID_SWITCH creates
/sys/class/switch directory, we disable symboling linking if
CONFIG_ANDROID_SWITCH is enabled.
If "CONFIG_ANDROID" is enabled, /sys/class/switch/* are created
as symbolic links to /sys/class/extcon/*.
The two files of switch class, name and state, are provided with
extcon, too. When the multistate support (STEP 2 of CHAPTER 1.) is

View File

@ -149,7 +149,7 @@ linux-api@ver.kernel.org に送ることを勧めます。
この他にパッチを作る方法についてのよくできた記述は-
"The Perfect Patch"
http://userweb.kernel.org/~akpm/stuff/tpp.txt
http://www.ozlabs.org/~akpm/stuff/tpp.txt
"Linux kernel patch submission format"
http://linux.yyz.us/patch-format.html
@ -622,7 +622,7 @@ Linux カーネルコミュニティは、一度に大量のコードの塊を
これについて全てがどのようにあるべきかについての詳細は、以下のドキュメ
ントの ChangeLog セクションを見てください-
"The Perfect Patch"
http://userweb.kernel.org/~akpm/stuff/tpp.txt
http://www.ozlabs.org/~akpm/stuff/tpp.txt
これらのどれもが、時にはとても困難です。これらの慣例を完璧に実施するに
は数年かかるかもしれません。これは継続的な改善のプロセスであり、そのた

View File

@ -122,7 +122,7 @@ mtk.manpages@gmail.com의 메인테이너에게 보낼 것을 권장한다.
올바른 패치들을 만드는 법에 관한 훌륭한 다른 문서들이 있다.
"The Perfect Patch"
http://userweb.kernel.org/~akpm/stuff/tpp.txt
http://www.ozlabs.org/~akpm/stuff/tpp.txt
"Linux kernel patch submission format"
http://linux.yyz.us/patch-format.html
@ -213,7 +213,7 @@ Documentation/DocBook/ 디렉토리 내에서 만들어지며 PDF, Postscript, H
것은 Linux Cross-Reference project이며 그것은 자기 참조 방식이며
소스코드를 인덱스된 웹 페이지들의 형태로 보여준다. 최신의 멋진 커널
코드 저장소는 다음을 통하여 참조할 수 있다.
http://users.sosdg.org/~qiyong/lxr/
http://lxr.linux.no/+trees
개발 프로세스
@ -222,20 +222,20 @@ Documentation/DocBook/ 디렉토리 내에서 만들어지며 PDF, Postscript, H
리눅스 커널 개발 프로세스는 현재 몇몇 다른 메인 커널 "브랜치들"과
서브시스템에 특화된 커널 브랜치들로 구성된다. 몇몇 다른 메인
브랜치들은 다음과 같다.
- main 2.6.x 커널 트리
- 2.6.x.y - 안정된 커널 트리
- 2.6.x -git 커널 패치들
- 2.6.x -mm 커널 패치들
- main 3.x 커널 트리
- 3.x.y - 안정된 커널 트리
- 3.x -git 커널 패치들
- 서브시스템을 위한 커널 트리들과 패치들
- 3.x - 통합 테스트를 위한 next 커널 트리
2.6.x 커널 트리
3.x 커널 트리
---------------
2.6.x 커널들은 Linux Torvalds가 관리하며 kernel.org의 pub/linux/kernel/v2.6/
3.x 커널들은 Linux Torvalds가 관리하며 kernel.org의 pub/linux/kernel/v3.x/
디렉토리에서 참조될 수 있다.개발 프로세스는 다음과 같다.
- 새로운 커널이 배포되자마자 2주의 시간이 주어진다. 이 기간동은
메인테이너들은 큰 diff들을 Linus에게 제출할 수 있다. 대개 이 패치들은
몇 주 동안 -mm 커널내에 이미 있었던 것들이다. 큰 변경들을 제출하는 데
몇 주 동안 -next 커널내에 이미 있었던 것들이다. 큰 변경들을 제출하는 데
선호되는 방법은 git(커널의 소스 관리 툴, 더 많은 정보들은 http://git.or.cz/
에서 참조할 수 있다)를 사용하는 것이지만 순수한 패치파일의 형식으로 보내는
것도 무관하다.
@ -262,20 +262,20 @@ Andrew Morton의 글이 있다.
버그의 상황에 따라 배포되는 것이지 미리정해 놓은 시간에 따라
배포되는 것은 아니기 때문이다."
2.6.x.y - 안정 커널 트리
3.x.y - 안정 커널 트리
------------------------
4 자리 숫자로 이루어진 버젼의 커널들은 -stable 커널들이다. 그것들은 2.6.x
3 자리 숫자로 이루어진 버젼의 커널들은 -stable 커널들이다. 그것들은 3.x
커널에서 발견된 큰 회귀들이나 보안 문제들 중 비교적 작고 중요한 수정들을
포함한다.
이것은 가장 최근의 안정적인 커널을 원하는 사용자에게 추천되는 브랜치이며,
개발/실험적 버젼을 테스트하는 것을 돕고자 하는 사용자들과는 별로 관련이 없다.
어떤 2.6.x.y 커널도 사용할 수 없다면 그때는 가장 높은 숫자의 2.6.x
어떤 3.x.y 커널도 사용할 수 없다면 그때는 가장 높은 숫자의 3.x
커널이 현재의 안정 커널이다.
2.6.x.y는 "stable" 팀<stable@kernel.org>에 의해 관리되며 거의 매번 격주로
3.x.y는 "stable" 팀<stable@vger.kernel.org>에 의해 관리되며 거의 매번 격주로
배포된다.
커널 트리 문서들 내에 Documentation/stable_kernel_rules.txt 파일은 어떤
@ -283,84 +283,46 @@ Andrew Morton의 글이 있다.
진행되는지를 설명한다.
2.6.x -git 패치들
3.x -git 패치들
------------------
git 저장소(그러므로 -git이라는 이름이 붙음)에는 날마다 관리되는 Linus의
커널 트리의 snapshot 들이 있다. 이 패치들은 일반적으로 날마다 배포되며
Linus의 트리의 현재 상태를 나타낸다. 이 패치들은 정상적인지 조금도
살펴보지 않고 자동적으로 생성된 것이므로 -rc 커널들 보다도 더 실험적이다.
2.6.x -mm 커널 패치들
---------------------
Andrew Morton에 의해 배포된 실험적인 커널 패치들이다. Andrew는 모든 다른
서브시스템 커널 트리와 패치들을 가져와서 리눅스 커널 메일링 리스트로
온 많은 패치들과 한데 묶는다. 이 트리는 새로운 기능들과 패치들을 위한
장소를 제공하는 역할을 한다. 하나의 패치가 -mm에 한동안 있으면서 그 가치가
증명되게 되면 Andrew나 서브시스템 메인테이너는 그것을 메인라인에 포함시키기
위하여 Linus에게 보낸다.
커널 트리에 포함하고 싶은 모든 새로운 패치들은 Linus에게 보내지기 전에
-mm 트리에서 테스트를 하는 것을 적극 추천한다.
이 커널들은 안정되게 사용할 시스템에서에 실행하는 것은 적합하지 않으며
다른 브랜치들의 어떤 것들보다 위험하다.
여러분이 커널 개발 프로세스를 돕길 원한다면 이 커널 배포들을 사용하고
테스트한 후 어떤 문제를 발견하거나 또는 모든 것이 잘 동작한다면 리눅스
커널 메일링 리스트로 피드백을 해달라.
이 커널들은 일반적으로 모든 다른 실험적인 패치들과 배포될 당시의
사용가능한 메인라인 -git 커널들의 몇몇 변경을 포함한다.
-mm 커널들은 정해진 일정대로 배포되지 않는다. 하지만 대개 몇몇 -mm 커널들은
각 -rc 커널(1부터 3이 흔함) 사이에서 배포된다.
서브시스템 커널 트리들과 패치들
-------------------------------
많은 다른 커널 서브시스템 개발자들은 커널의 다른 부분들에서 무슨 일이
일어나고 있는지를 볼수 있도록 그들의 개발 트리를 공개한다. 이 트리들은
위에서 설명하였던 것 처럼 -mm 커널 배포들로 합쳐진다.
다양한 커널 서브시스템의 메인테이너들 --- 그리고 많은 커널 서브시스템 개발자들
--- 은 그들의 현재 개발 상태를 소스 저장소로 노출한다. 이를 통해 다른 사람들도
커널의 다른 영역에 어떤 변화가 이루어지고 있는지 알 수 있다. 급속히 개발이
진행되는 영역이 있고 그렇지 않은 영역이 있으므로, 개발자는 다른 개발자가 제출한
수정 사항과 자신의 수정사항의 충돌이나 동일한 일을 동시에 두사람이 따로
진행하는 사태를 방지하기 위해 급속히 개발이 진행되고 있는 영역에 작업의
베이스를 맞춰줄 것이 요구된다.
다음은 활용가능한 커널 트리들을 나열한다.
git trees:
- Kbuild development tree, Sam Ravnborg < sam@ravnborg.org>
git.kernel.org:/pub/scm/linux/kernel/git/sam/kbuild.git
대부분의 이러한 저장소는 git 트리지만, git이 아닌 SCM으로 관리되거나, quilt
시리즈로 제공되는 패치들도 존재한다. 이러한 서브시스템 저장소들은 MAINTAINERS
파일에 나열되어 있다. 대부분은 http://git.kernel.org 에서 볼 수 있다.
- ACPI development tree, Len Brown <len.brown@intel.com >
git.kernel.org:/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6.git
제안된 패치는 서브시스템 트리에 커밋되기 전에 메일링 리스트를 통해
리뷰된다(아래의 관련 섹션을 참고하기 바란다). 일부 커널 서브시스템의 경우, 이
리뷰 프로세스는 patchwork라는 도구를 통해 추적된다. patchwork은 등록된 패치와
패치에 대한 코멘트, 패치의 버전을 볼 수 있는 웹 인터페이스를 제공하고,
메인테이너는 패치를 리뷰 중, 리뷰 통과, 또는 반려됨으로 표시할 수 있다.
대부분의 이러한 patchwork 사이트는 http://patchwork.kernel.org/ 또는
http://patchwork.ozlabs.org/ 에 나열되어 있다.
- Block development tree, Jens Axboe <jens.axboe@oracle.com>
git.kernel.org:/pub/scm/linux/kernel/git/axboe/linux-2.6-block.git
3.x - 통합 테스트를 위한 next 커널 트리
-----------------------------------------
서브시스템 트리들의 변경사항들은 mainline 3.x 트리로 들어오기 전에 통합
테스트를 거쳐야 한다. 이런 목적으로, 모든 서브시스템 트리의 변경사항을 거의
매일 받아가는 특수한 테스트 저장소가 존재한다:
http://git.kernel.org/?p=linux/kernel/git/sfr/linux-next.git
http://linux.f-seidel.de/linux-next/pmwiki/
- DRM development tree, Dave Airlie <airlied@linux.ie>
git.kernel.org:/pub/scm/linux/kernel/git/airlied/drm-2.6.git
- ia64 development tree, Tony Luck < tony.luck@intel.com>
git.kernel.org:/pub/scm/linux/kernel/git/aegl/linux-2.6.git
- infiniband, Roland Dreier <rolandd@cisco.com >
git.kernel.org:/pub/scm/linux/kernel/git/roland/infiniband.git
- libata, Jeff Garzik <jgarzik@pobox.com>
git.kernel.org:/pub/scm/linux/kernel/git/jgarzik/libata-dev.git
- network drivers, Jeff Garzik <jgarzik@pobox.com>
git.kernel.org:/pub/scm/linux/kernel/git/jgarzik/netdev-2.6.git
- pcmcia, Dominik Brodowski < linux@dominikbrodowski.net>
git.kernel.org:/pub/scm/linux/kernel/git/brodo/pcmcia-2.6.git
- SCSI, James Bottomley < James.Bottomley@SteelEye.com>
git.kernel.org:/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6.git
quilt trees:
- USB, PCI, Driver Core, and I2C, Greg Kroah-Hartman < gregkh@linuxfoundation.org>
kernel.org/pub/linux/kernel/people/gregkh/gregkh-2.6/
- x86-64, partly i386, Andi Kleen < ak@suse.de>
ftp.firstfloor.org:/pub/ak/x86_64/quilt/
다른 커널 트리들은 http://kernel.org/git와 MAINTAINERS 파일에서 참조할 수
있다.
이런 식으로, -next 커널을 통해 다음 머지 기간에 메인라인 커널에 어떤 변경이
가해질 것인지 간략히 알 수 있다. 모험심 강한 테스터라면 -next 커널에서 테스트를
수행하는 것도 좋을 것이다.
버그 보고
---------
@ -597,7 +559,7 @@ Pat이라는 이름을 가진 여자가 있을 수도 있는 것이다. 리눅
이것이 무엇인지 더 자세한 것을 알고 싶다면 다음 문서의 ChageLog 항을 봐라.
"The Perfect Patch"
http://userweb.kernel.org/~akpm/stuff/tpp.txt
http://www.ozlabs.org/~akpm/stuff/tpp.txt

View File

@ -115,8 +115,6 @@ static bool mei_init(struct mei *me, const uuid_le *guid,
struct mei_client *cl;
struct mei_connect_client_data data;
mei_deinit(me);
me->verbose = verbose;
me->fd = open("/dev/mei", O_RDWR);

View File

@ -112,7 +112,7 @@ Linux内核代码中包含有大量的文档。这些文档对于学习如何与
其他关于如何正确地生成补丁的优秀文档包括:
"The Perfect Patch"
http://userweb.kernel.org/~akpm/stuff/tpp.txt
http://www.ozlabs.org/~akpm/stuff/tpp.txt
"Linux kernel patch submission format"
http://linux.yyz.us/patch-format.html
@ -515,7 +515,7 @@ Linux内核社区并不喜欢一下接收大段的代码。修改需要被恰当
想了解它具体应该看起来像什么请查阅以下文档中的“ChangeLog”章节
“The Perfect Patch”
http://userweb.kernel.org/~akpm/stuff/tpp.txt
http://www.ozlabs.org/~akpm/stuff/tpp.txt
这些事情有时候做起来很难。要在任何方面都做到完美可能需要好几年时间。这是

View File

@ -2619,7 +2619,7 @@ S: Maintained
F: drivers/platform/x86/dell-laptop.c
DELL LAPTOP SMM DRIVER
S: Orphan
M: Guenter Roeck <linux@roeck-us.net>
F: drivers/char/i8k.c
F: include/uapi/linux/i8k.h
@ -3335,6 +3335,7 @@ EXTERNAL CONNECTOR SUBSYSTEM (EXTCON)
M: MyungJoo Ham <myungjoo.ham@samsung.com>
M: Chanwoo Choi <cw00.choi@samsung.com>
L: linux-kernel@vger.kernel.org
T: git git://git.kernel.org/pub/scm/linux/kernel/git/chanwoo/extcon.git
S: Maintained
F: drivers/extcon/
F: Documentation/extcon/

View File

@ -735,7 +735,7 @@ static struct pci_device_id agp_amd64_pci_table[] = {
MODULE_DEVICE_TABLE(pci, agp_amd64_pci_table);
static DEFINE_PCI_DEVICE_TABLE(agp_amd64_pci_promisc_table) = {
static const struct pci_device_id agp_amd64_pci_promisc_table[] = {
{ PCI_DEVICE_CLASS(0, 0) },
{ }
};

View File

@ -1,12 +1,11 @@
/*
* i8k.c -- Linux driver for accessing the SMM BIOS on Dell laptops.
* See http://www.debian.org/~dz/i8k/ for more information
* and for latest version of this driver.
*
* Copyright (C) 2001 Massimo Dal Zotto <dz@debian.org>
*
* Hwmon integration:
* Copyright (C) 2011 Jean Delvare <khali@linux-fr.org>
* Copyright (C) 2013 Guenter Roeck <linux@roeck-us.net>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
@ -19,6 +18,8 @@
* General Public License for more details.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
@ -29,13 +30,12 @@
#include <linux/mutex.h>
#include <linux/hwmon.h>
#include <linux/hwmon-sysfs.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/sched.h>
#include <linux/i8k.h>
#define I8K_VERSION "1.14 21/02/2005"
#define I8K_SMM_FN_STATUS 0x0025
#define I8K_SMM_POWER_STATUS 0x0069
#define I8K_SMM_SET_FAN 0x01a3
@ -44,7 +44,6 @@
#define I8K_SMM_GET_TEMP 0x10a3
#define I8K_SMM_GET_DELL_SIG1 0xfea3
#define I8K_SMM_GET_DELL_SIG2 0xffa3
#define I8K_SMM_BIOS_VERSION 0x00a6
#define I8K_FAN_MULT 30
#define I8K_MAX_TEMP 127
@ -64,6 +63,15 @@
static DEFINE_MUTEX(i8k_mutex);
static char bios_version[4];
static struct device *i8k_hwmon_dev;
static u32 i8k_hwmon_flags;
static int i8k_fan_mult;
#define I8K_HWMON_HAVE_TEMP1 (1 << 0)
#define I8K_HWMON_HAVE_TEMP2 (1 << 1)
#define I8K_HWMON_HAVE_TEMP3 (1 << 2)
#define I8K_HWMON_HAVE_TEMP4 (1 << 3)
#define I8K_HWMON_HAVE_FAN1 (1 << 4)
#define I8K_HWMON_HAVE_FAN2 (1 << 5)
MODULE_AUTHOR("Massimo Dal Zotto (dz@debian.org)");
MODULE_DESCRIPTION("Driver for accessing SMM BIOS on Dell laptops");
@ -103,11 +111,11 @@ static const struct file_operations i8k_fops = {
struct smm_regs {
unsigned int eax;
unsigned int ebx __attribute__ ((packed));
unsigned int ecx __attribute__ ((packed));
unsigned int edx __attribute__ ((packed));
unsigned int esi __attribute__ ((packed));
unsigned int edi __attribute__ ((packed));
unsigned int ebx __packed;
unsigned int ecx __packed;
unsigned int edx __packed;
unsigned int esi __packed;
unsigned int edi __packed;
};
static inline const char *i8k_get_dmi_data(int field)
@ -124,6 +132,17 @@ static int i8k_smm(struct smm_regs *regs)
{
int rc;
int eax = regs->eax;
cpumask_var_t old_mask;
/* SMM requires CPU 0 */
if (!alloc_cpumask_var(&old_mask, GFP_KERNEL))
return -ENOMEM;
cpumask_copy(old_mask, &current->cpus_allowed);
set_cpus_allowed_ptr(current, cpumask_of(0));
if (smp_processor_id() != 0) {
rc = -EBUSY;
goto out;
}
#if defined(CONFIG_X86_64)
asm volatile("pushq %%rax\n\t"
@ -148,7 +167,7 @@ static int i8k_smm(struct smm_regs *regs)
"pushfq\n\t"
"popq %%rax\n\t"
"andl $1,%%eax\n"
:"=a"(rc)
: "=a"(rc)
: "a"(regs)
: "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
#else
@ -174,25 +193,17 @@ static int i8k_smm(struct smm_regs *regs)
"lahf\n\t"
"shrl $8,%%eax\n\t"
"andl $1,%%eax\n"
:"=a"(rc)
: "=a"(rc)
: "a"(regs)
: "%ebx", "%ecx", "%edx", "%esi", "%edi", "memory");
#endif
if (rc != 0 || (regs->eax & 0xffff) == 0xffff || regs->eax == eax)
return -EINVAL;
rc = -EINVAL;
return 0;
}
/*
* Read the bios version. Return the version as an integer corresponding
* to the ascii value, for example "A17" is returned as 0x00413137.
*/
static int i8k_get_bios_version(void)
{
struct smm_regs regs = { .eax = I8K_SMM_BIOS_VERSION, };
return i8k_smm(&regs) ? : regs.eax;
out:
set_cpus_allowed_ptr(current, old_mask);
free_cpumask_var(old_mask);
return rc;
}
/*
@ -203,7 +214,8 @@ static int i8k_get_fn_status(void)
struct smm_regs regs = { .eax = I8K_SMM_FN_STATUS, };
int rc;
if ((rc = i8k_smm(&regs)) < 0)
rc = i8k_smm(&regs);
if (rc < 0)
return rc;
switch ((regs.eax >> I8K_FN_SHIFT) & I8K_FN_MASK) {
@ -226,7 +238,8 @@ static int i8k_get_power_status(void)
struct smm_regs regs = { .eax = I8K_SMM_POWER_STATUS, };
int rc;
if ((rc = i8k_smm(&regs)) < 0)
rc = i8k_smm(&regs);
if (rc < 0)
return rc;
return (regs.eax & 0xff) == I8K_POWER_AC ? I8K_AC : I8K_BATTERY;
@ -251,7 +264,7 @@ static int i8k_get_fan_speed(int fan)
struct smm_regs regs = { .eax = I8K_SMM_GET_SPEED, };
regs.ebx = fan & 0xff;
return i8k_smm(&regs) ? : (regs.eax & 0xffff) * fan_mult;
return i8k_smm(&regs) ? : (regs.eax & 0xffff) * i8k_fan_mult;
}
/*
@ -277,10 +290,11 @@ static int i8k_get_temp(int sensor)
int temp;
#ifdef I8K_TEMPERATURE_BUG
static int prev;
static int prev[4];
#endif
regs.ebx = sensor & 0xff;
if ((rc = i8k_smm(&regs)) < 0)
rc = i8k_smm(&regs);
if (rc < 0)
return rc;
temp = regs.eax & 0xff;
@ -294,10 +308,10 @@ static int i8k_get_temp(int sensor)
# 1003655139 00000054 00005c52
*/
if (temp > I8K_MAX_TEMP) {
temp = prev;
prev = I8K_MAX_TEMP;
temp = prev[sensor];
prev[sensor] = I8K_MAX_TEMP;
} else {
prev = temp;
prev[sensor] = temp;
}
#endif
@ -309,7 +323,8 @@ static int i8k_get_dell_signature(int req_fn)
struct smm_regs regs = { .eax = req_fn, };
int rc;
if ((rc = i8k_smm(&regs)) < 0)
rc = i8k_smm(&regs);
if (rc < 0)
return rc;
return regs.eax == 1145651527 && regs.edx == 1145392204 ? 0 : -1;
@ -328,12 +343,14 @@ i8k_ioctl_unlocked(struct file *fp, unsigned int cmd, unsigned long arg)
switch (cmd) {
case I8K_BIOS_VERSION:
val = i8k_get_bios_version();
val = (bios_version[0] << 16) |
(bios_version[1] << 8) | bios_version[2];
break;
case I8K_MACHINE_ID:
memset(buff, 0, 16);
strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL), sizeof(buff));
strlcpy(buff, i8k_get_dmi_data(DMI_PRODUCT_SERIAL),
sizeof(buff));
break;
case I8K_FN_STATUS:
@ -470,12 +487,13 @@ static ssize_t i8k_hwmon_show_temp(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
int cpu_temp;
int index = to_sensor_dev_attr(devattr)->index;
int temp;
cpu_temp = i8k_get_temp(0);
if (cpu_temp < 0)
return cpu_temp;
return sprintf(buf, "%d\n", cpu_temp * 1000);
temp = i8k_get_temp(index);
if (temp < 0)
return temp;
return sprintf(buf, "%d\n", temp * 1000);
}
static ssize_t i8k_hwmon_show_fan(struct device *dev,
@ -491,12 +509,44 @@ static ssize_t i8k_hwmon_show_fan(struct device *dev,
return sprintf(buf, "%d\n", fan_speed);
}
static ssize_t i8k_hwmon_show_pwm(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
int index = to_sensor_dev_attr(devattr)->index;
int status;
status = i8k_get_fan_status(index);
if (status < 0)
return -EIO;
return sprintf(buf, "%d\n", clamp_val(status * 128, 0, 255));
}
static ssize_t i8k_hwmon_set_pwm(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int index = to_sensor_dev_attr(attr)->index;
unsigned long val;
int err;
err = kstrtoul(buf, 10, &val);
if (err)
return err;
val = clamp_val(DIV_ROUND_CLOSEST(val, 128), 0, 2);
mutex_lock(&i8k_mutex);
err = i8k_set_fan(index, val);
mutex_unlock(&i8k_mutex);
return err < 0 ? -EIO : count;
}
static ssize_t i8k_hwmon_show_label(struct device *dev,
struct device_attribute *devattr,
char *buf)
{
static const char *labels[4] = {
"i8k",
static const char *labels[3] = {
"CPU",
"Left Fan",
"Right Fan",
@ -506,108 +556,108 @@ static ssize_t i8k_hwmon_show_label(struct device *dev,
return sprintf(buf, "%s\n", labels[index]);
}
static DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL);
static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 0);
static SENSOR_DEVICE_ATTR(temp2_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 1);
static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 2);
static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 3);
static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
I8K_FAN_LEFT);
static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm,
i8k_hwmon_set_pwm, I8K_FAN_LEFT);
static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL,
I8K_FAN_RIGHT);
static SENSOR_DEVICE_ATTR(name, S_IRUGO, i8k_hwmon_show_label, NULL, 0);
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1);
static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2);
static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 3);
static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm,
i8k_hwmon_set_pwm, I8K_FAN_RIGHT);
static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 0);
static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1);
static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2);
static void i8k_hwmon_remove_files(struct device *dev)
static struct attribute *i8k_attrs[] = {
&sensor_dev_attr_temp1_input.dev_attr.attr, /* 0 */
&sensor_dev_attr_temp1_label.dev_attr.attr, /* 1 */
&sensor_dev_attr_temp2_input.dev_attr.attr, /* 2 */
&sensor_dev_attr_temp3_input.dev_attr.attr, /* 3 */
&sensor_dev_attr_temp4_input.dev_attr.attr, /* 4 */
&sensor_dev_attr_fan1_input.dev_attr.attr, /* 5 */
&sensor_dev_attr_pwm1.dev_attr.attr, /* 6 */
&sensor_dev_attr_fan1_label.dev_attr.attr, /* 7 */
&sensor_dev_attr_fan2_input.dev_attr.attr, /* 8 */
&sensor_dev_attr_pwm2.dev_attr.attr, /* 9 */
&sensor_dev_attr_fan2_label.dev_attr.attr, /* 10 */
NULL
};
static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr,
int index)
{
device_remove_file(dev, &dev_attr_temp1_input);
device_remove_file(dev, &sensor_dev_attr_fan1_input.dev_attr);
device_remove_file(dev, &sensor_dev_attr_fan2_input.dev_attr);
device_remove_file(dev, &sensor_dev_attr_temp1_label.dev_attr);
device_remove_file(dev, &sensor_dev_attr_fan1_label.dev_attr);
device_remove_file(dev, &sensor_dev_attr_fan2_label.dev_attr);
device_remove_file(dev, &sensor_dev_attr_name.dev_attr);
if ((index == 0 || index == 1) &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP1))
return 0;
if (index == 2 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP2))
return 0;
if (index == 3 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP3))
return 0;
if (index == 4 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4))
return 0;
if (index >= 5 && index <= 7 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1))
return 0;
if (index >= 8 && index <= 10 &&
!(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2))
return 0;
return attr->mode;
}
static const struct attribute_group i8k_group = {
.attrs = i8k_attrs,
.is_visible = i8k_is_visible,
};
__ATTRIBUTE_GROUPS(i8k);
static int __init i8k_init_hwmon(void)
{
int err;
i8k_hwmon_dev = hwmon_device_register(NULL);
if (IS_ERR(i8k_hwmon_dev)) {
err = PTR_ERR(i8k_hwmon_dev);
i8k_hwmon_dev = NULL;
printk(KERN_ERR "i8k: hwmon registration failed (%d)\n", err);
return err;
}
/* Required name attribute */
err = device_create_file(i8k_hwmon_dev,
&sensor_dev_attr_name.dev_attr);
if (err)
goto exit_unregister;
i8k_hwmon_flags = 0;
/* CPU temperature attributes, if temperature reading is OK */
err = i8k_get_temp(0);
if (err < 0) {
dev_dbg(i8k_hwmon_dev,
"Not creating temperature attributes (%d)\n", err);
} else {
err = device_create_file(i8k_hwmon_dev, &dev_attr_temp1_input);
if (err)
goto exit_remove_files;
err = device_create_file(i8k_hwmon_dev,
&sensor_dev_attr_temp1_label.dev_attr);
if (err)
goto exit_remove_files;
}
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP1;
/* check for additional temperature sensors */
err = i8k_get_temp(1);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP2;
err = i8k_get_temp(2);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP3;
err = i8k_get_temp(3);
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_TEMP4;
/* Left fan attributes, if left fan is present */
err = i8k_get_fan_status(I8K_FAN_LEFT);
if (err < 0) {
dev_dbg(i8k_hwmon_dev,
"Not creating %s fan attributes (%d)\n", "left", err);
} else {
err = device_create_file(i8k_hwmon_dev,
&sensor_dev_attr_fan1_input.dev_attr);
if (err)
goto exit_remove_files;
err = device_create_file(i8k_hwmon_dev,
&sensor_dev_attr_fan1_label.dev_attr);
if (err)
goto exit_remove_files;
}
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN1;
/* Right fan attributes, if right fan is present */
err = i8k_get_fan_status(I8K_FAN_RIGHT);
if (err < 0) {
dev_dbg(i8k_hwmon_dev,
"Not creating %s fan attributes (%d)\n", "right", err);
} else {
err = device_create_file(i8k_hwmon_dev,
&sensor_dev_attr_fan2_input.dev_attr);
if (err)
goto exit_remove_files;
err = device_create_file(i8k_hwmon_dev,
&sensor_dev_attr_fan2_label.dev_attr);
if (err)
goto exit_remove_files;
if (err >= 0)
i8k_hwmon_flags |= I8K_HWMON_HAVE_FAN2;
i8k_hwmon_dev = hwmon_device_register_with_groups(NULL, "i8k", NULL,
i8k_groups);
if (IS_ERR(i8k_hwmon_dev)) {
err = PTR_ERR(i8k_hwmon_dev);
i8k_hwmon_dev = NULL;
pr_err("hwmon registration failed (%d)\n", err);
return err;
}
return 0;
exit_remove_files:
i8k_hwmon_remove_files(i8k_hwmon_dev);
exit_unregister:
hwmon_device_unregister(i8k_hwmon_dev);
return err;
}
static void __exit i8k_exit_hwmon(void)
{
i8k_hwmon_remove_files(i8k_hwmon_dev);
hwmon_device_unregister(i8k_hwmon_dev);
}
static struct dmi_system_id __initdata i8k_dmi_table[] = {
static struct dmi_system_id i8k_dmi_table[] __initdata = {
{
.ident = "Dell Inspiron",
.matches = {
@ -671,7 +721,23 @@ static struct dmi_system_id __initdata i8k_dmi_table[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "XPS L421X"),
},
},
{ }
{
.ident = "Dell Studio",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "Studio"),
},
.driver_data = (void *)1, /* fan multiplier override */
},
{
.ident = "Dell XPS M140",
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
DMI_MATCH(DMI_PRODUCT_NAME, "MXC051"),
},
.driver_data = (void *)1, /* fan multiplier override */
},
{ }
};
/*
@ -679,8 +745,7 @@ static struct dmi_system_id __initdata i8k_dmi_table[] = {
*/
static int __init i8k_probe(void)
{
char buff[4];
int version;
const struct dmi_system_id *id;
/*
* Get DMI information
@ -689,49 +754,30 @@ static int __init i8k_probe(void)
if (!ignore_dmi && !force)
return -ENODEV;
printk(KERN_INFO "i8k: not running on a supported Dell system.\n");
printk(KERN_INFO "i8k: vendor=%s, model=%s, version=%s\n",
pr_info("not running on a supported Dell system.\n");
pr_info("vendor=%s, model=%s, version=%s\n",
i8k_get_dmi_data(DMI_SYS_VENDOR),
i8k_get_dmi_data(DMI_PRODUCT_NAME),
i8k_get_dmi_data(DMI_BIOS_VERSION));
}
strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION), sizeof(bios_version));
strlcpy(bios_version, i8k_get_dmi_data(DMI_BIOS_VERSION),
sizeof(bios_version));
/*
* Get SMM Dell signature
*/
if (i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG1) &&
i8k_get_dell_signature(I8K_SMM_GET_DELL_SIG2)) {
printk(KERN_ERR "i8k: unable to get SMM Dell signature\n");
pr_err("unable to get SMM Dell signature\n");
if (!force)
return -ENODEV;
}
/*
* Get SMM BIOS version.
*/
version = i8k_get_bios_version();
if (version <= 0) {
printk(KERN_WARNING "i8k: unable to get SMM BIOS version\n");
} else {
buff[0] = (version >> 16) & 0xff;
buff[1] = (version >> 8) & 0xff;
buff[2] = (version) & 0xff;
buff[3] = '\0';
/*
* If DMI BIOS version is unknown use SMM BIOS version.
*/
if (!dmi_get_system_info(DMI_BIOS_VERSION))
strlcpy(bios_version, buff, sizeof(bios_version));
/*
* Check if the two versions match.
*/
if (strncmp(buff, bios_version, sizeof(bios_version)) != 0)
printk(KERN_WARNING "i8k: BIOS version mismatch: %s != %s\n",
buff, bios_version);
}
i8k_fan_mult = fan_mult;
id = dmi_first_match(i8k_dmi_table);
if (id && fan_mult == I8K_FAN_MULT && id->driver_data)
i8k_fan_mult = (unsigned long)id->driver_data;
return 0;
}
@ -754,10 +800,6 @@ static int __init i8k_init(void)
if (err)
goto exit_remove_proc;
printk(KERN_INFO
"Dell laptop SMM driver v%s Massimo Dal Zotto (dz@debian.org)\n",
I8K_VERSION);
return 0;
exit_remove_proc:
@ -767,7 +809,7 @@ static int __init i8k_init(void)
static void __exit i8k_exit(void)
{
i8k_exit_hwmon();
hwmon_device_unregister(i8k_hwmon_dev);
remove_proc_entry("i8k", NULL);
}

View File

@ -587,6 +587,8 @@ static int lp_do_ioctl(unsigned int minor, unsigned int cmd,
return -ENODEV;
switch ( cmd ) {
case LPTIME:
if (arg > UINT_MAX / HZ)
return -EINVAL;
LP_TIME(minor) = arg * HZ/100;
break;
case LPCHAR:

View File

@ -168,7 +168,10 @@ static irqreturn_t button_handler (int irq, void *dev_id)
static int button_read (struct file *filp, char __user *buffer,
size_t count, loff_t *ppos)
{
interruptible_sleep_on (&button_wait_queue);
DEFINE_WAIT(wait);
prepare_to_wait(&button_wait_queue, &wait, TASK_INTERRUPTIBLE);
schedule();
finish_wait(&button_wait_queue, &wait);
return (copy_to_user (buffer, &button_output_buffer, bcount))
? -EFAULT : bcount;
}

View File

@ -216,4 +216,4 @@ error:
ttyprintk_driver = NULL;
return ret;
}
module_init(ttyprintk_init);
device_initcall(ttyprintk_init);

View File

@ -31,6 +31,16 @@ config EXTCON_ADC_JACK
help
Say Y here to enable extcon device driver based on ADC values.
config EXTCON_MAX14577
tristate "MAX14577 EXTCON Support"
depends on MFD_MAX14577
select IRQ_DOMAIN
select REGMAP_I2C
help
If you say yes here you get support for the MUIC device of
Maxim MAX14577 PMIC. The MAX14577 MUIC is a USB port accessory
detector and switch.
config EXTCON_MAX77693
tristate "MAX77693 EXTCON Support"
depends on MFD_MAX77693 && INPUT

View File

@ -7,6 +7,7 @@ obj-$(CONFIG_OF_EXTCON) += of_extcon.o
obj-$(CONFIG_EXTCON) += extcon-class.o
obj-$(CONFIG_EXTCON_GPIO) += extcon-gpio.o
obj-$(CONFIG_EXTCON_ADC_JACK) += extcon-adc-jack.o
obj-$(CONFIG_EXTCON_MAX14577) += extcon-max14577.o
obj-$(CONFIG_EXTCON_MAX77693) += extcon-max77693.o
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o

View File

@ -44,6 +44,15 @@
#define HPDET_DEBOUNCE 500
#define DEFAULT_MICD_TIMEOUT 2000
#define MICD_LVL_1_TO_7 (ARIZONA_MICD_LVL_1 | ARIZONA_MICD_LVL_2 | \
ARIZONA_MICD_LVL_3 | ARIZONA_MICD_LVL_4 | \
ARIZONA_MICD_LVL_5 | ARIZONA_MICD_LVL_6 | \
ARIZONA_MICD_LVL_7)
#define MICD_LVL_0_TO_7 (ARIZONA_MICD_LVL_0 | MICD_LVL_1_TO_7)
#define MICD_LVL_0_TO_8 (MICD_LVL_0_TO_7 | ARIZONA_MICD_LVL_8)
struct arizona_extcon_info {
struct device *dev;
struct arizona *arizona;
@ -426,26 +435,15 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
}
val &= ARIZONA_HP_LVL_B_MASK;
/* Convert to ohms, the value is in 0.5 ohm increments */
val /= 2;
regmap_read(arizona->regmap, ARIZONA_HEADPHONE_DETECT_1,
&range);
range = (range & ARIZONA_HP_IMPEDANCE_RANGE_MASK)
>> ARIZONA_HP_IMPEDANCE_RANGE_SHIFT;
/* Skip up or down a range? */
if (range && (val < arizona_hpdet_c_ranges[range].min)) {
range--;
dev_dbg(arizona->dev, "Moving to HPDET range %d-%d\n",
arizona_hpdet_c_ranges[range].min,
arizona_hpdet_c_ranges[range].max);
regmap_update_bits(arizona->regmap,
ARIZONA_HEADPHONE_DETECT_1,
ARIZONA_HP_IMPEDANCE_RANGE_MASK,
range <<
ARIZONA_HP_IMPEDANCE_RANGE_SHIFT);
return -EAGAIN;
}
/* Skip up a range, or report? */
if (range < ARRAY_SIZE(arizona_hpdet_c_ranges) - 1 &&
(val >= arizona_hpdet_c_ranges[range].max)) {
range++;
@ -459,6 +457,12 @@ static int arizona_hpdet_read(struct arizona_extcon_info *info)
ARIZONA_HP_IMPEDANCE_RANGE_SHIFT);
return -EAGAIN;
}
if (range && (val < arizona_hpdet_c_ranges[range].min)) {
dev_dbg(arizona->dev, "Reporting range boundary %d\n",
arizona_hpdet_c_ranges[range].min);
val = arizona_hpdet_c_ranges[range].min;
}
}
dev_dbg(arizona->dev, "HP impedance %d ohms\n", val);
@ -594,9 +598,15 @@ static irqreturn_t arizona_hpdet_irq(int irq, void *data)
dev_err(arizona->dev, "Failed to report HP/line: %d\n",
ret);
done:
/* Reset back to starting range */
regmap_update_bits(arizona->regmap,
ARIZONA_HEADPHONE_DETECT_1,
ARIZONA_HP_IMPEDANCE_RANGE_MASK | ARIZONA_HP_POLL,
0);
arizona_extcon_do_magic(info, 0);
done:
if (id_gpio)
gpio_set_value_cansleep(id_gpio, 0);
@ -765,7 +775,20 @@ static void arizona_micd_detect(struct work_struct *work)
mutex_lock(&info->lock);
for (i = 0; i < 10 && !(val & 0x7fc); i++) {
/* If the cable was removed while measuring ignore the result */
ret = extcon_get_cable_state_(&info->edev, ARIZONA_CABLE_MECHANICAL);
if (ret < 0) {
dev_err(arizona->dev, "Failed to check cable state: %d\n",
ret);
mutex_unlock(&info->lock);
return;
} else if (!ret) {
dev_dbg(arizona->dev, "Ignoring MICDET for removed cable\n");
mutex_unlock(&info->lock);
return;
}
for (i = 0; i < 10 && !(val & MICD_LVL_0_TO_8); i++) {
ret = regmap_read(arizona->regmap, ARIZONA_MIC_DETECT_3, &val);
if (ret != 0) {
dev_err(arizona->dev,
@ -784,7 +807,7 @@ static void arizona_micd_detect(struct work_struct *work)
}
}
if (i == 10 && !(val & 0x7fc)) {
if (i == 10 && !(val & MICD_LVL_0_TO_8)) {
dev_err(arizona->dev, "Failed to get valid MICDET value\n");
mutex_unlock(&info->lock);
return;
@ -798,7 +821,7 @@ static void arizona_micd_detect(struct work_struct *work)
}
/* If we got a high impedence we should have a headset, report it. */
if (info->detecting && (val & 0x400)) {
if (info->detecting && (val & ARIZONA_MICD_LVL_8)) {
arizona_identify_headphone(info);
ret = extcon_update_state(&info->edev,
@ -827,7 +850,7 @@ static void arizona_micd_detect(struct work_struct *work)
* plain headphones. If both polarities report a low
* impedence then give up and report headphones.
*/
if (info->detecting && (val & 0x3f8)) {
if (info->detecting && (val & MICD_LVL_1_TO_7)) {
if (info->jack_flips >= info->micd_num_modes * 10) {
dev_dbg(arizona->dev, "Detected HP/line\n");
arizona_identify_headphone(info);
@ -851,7 +874,7 @@ static void arizona_micd_detect(struct work_struct *work)
* If we're still detecting and we detect a short then we've
* got a headphone. Otherwise it's a button press.
*/
if (val & 0x3fc) {
if (val & MICD_LVL_0_TO_7) {
if (info->mic) {
dev_dbg(arizona->dev, "Mic button detected\n");
@ -1126,6 +1149,16 @@ static int arizona_extcon_probe(struct platform_device *pdev)
break;
}
break;
case WM5110:
switch (arizona->rev) {
case 0 ... 2:
break;
default:
info->micd_clamp = true;
info->hpdet_ip = 2;
break;
}
break;
default:
break;
}

View File

@ -40,6 +40,7 @@ struct gpio_extcon_data {
int irq;
struct delayed_work work;
unsigned long debounce_jiffies;
bool check_on_resume;
};
static void gpio_extcon_work(struct work_struct *work)
@ -103,8 +104,15 @@ static int gpio_extcon_probe(struct platform_device *pdev)
extcon_data->gpio_active_low = pdata->gpio_active_low;
extcon_data->state_on = pdata->state_on;
extcon_data->state_off = pdata->state_off;
extcon_data->check_on_resume = pdata->check_on_resume;
if (pdata->state_on && pdata->state_off)
extcon_data->edev.print_state = extcon_gpio_print_state;
ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN,
pdev->name);
if (ret < 0)
return ret;
if (pdata->debounce) {
ret = gpio_set_debounce(extcon_data->gpio,
pdata->debounce * 1000);
@ -117,11 +125,6 @@ static int gpio_extcon_probe(struct platform_device *pdev)
if (ret < 0)
return ret;
ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN,
pdev->name);
if (ret < 0)
goto err;
INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);
extcon_data->irq = gpio_to_irq(extcon_data->gpio);
@ -159,12 +162,31 @@ static int gpio_extcon_remove(struct platform_device *pdev)
return 0;
}
#ifdef CONFIG_PM_SLEEP
static int gpio_extcon_resume(struct device *dev)
{
struct gpio_extcon_data *extcon_data;
extcon_data = dev_get_drvdata(dev);
if (extcon_data->check_on_resume)
queue_delayed_work(system_power_efficient_wq,
&extcon_data->work, extcon_data->debounce_jiffies);
return 0;
}
#endif
static const struct dev_pm_ops gpio_extcon_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(NULL, gpio_extcon_resume)
};
static struct platform_driver gpio_extcon_driver = {
.probe = gpio_extcon_probe,
.remove = gpio_extcon_remove,
.driver = {
.name = "extcon-gpio",
.owner = THIS_MODULE,
.pm = &gpio_extcon_pm_ops,
},
};

View File

@ -0,0 +1,752 @@
/*
* extcon-max14577.c - MAX14577 extcon driver to support MAX14577 MUIC
*
* Copyright (C) 2013 Samsung Electrnoics
* Chanwoo Choi <cw00.choi@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/interrupt.h>
#include <linux/platform_device.h>
#include <linux/mfd/max14577.h>
#include <linux/mfd/max14577-private.h>
#include <linux/extcon.h>
#define DEV_NAME "max14577-muic"
#define DELAY_MS_DEFAULT 17000 /* unit: millisecond */
enum max14577_muic_adc_debounce_time {
ADC_DEBOUNCE_TIME_5MS = 0,
ADC_DEBOUNCE_TIME_10MS,
ADC_DEBOUNCE_TIME_25MS,
ADC_DEBOUNCE_TIME_38_62MS,
};
enum max14577_muic_status {
MAX14577_MUIC_STATUS1 = 0,
MAX14577_MUIC_STATUS2 = 1,
MAX14577_MUIC_STATUS_END,
};
struct max14577_muic_info {
struct device *dev;
struct max14577 *max14577;
struct extcon_dev *edev;
int prev_cable_type;
int prev_chg_type;
u8 status[MAX14577_MUIC_STATUS_END];
bool irq_adc;
bool irq_chg;
struct work_struct irq_work;
struct mutex mutex;
/*
* Use delayed workqueue to detect cable state and then
* notify cable state to notifiee/platform through uevent.
* After completing the booting of platform, the extcon provider
* driver should notify cable state to upper layer.
*/
struct delayed_work wq_detcable;
/*
* Default usb/uart path whether UART/USB or AUX_UART/AUX_USB
* h/w path of COMP2/COMN1 on CONTROL1 register.
*/
int path_usb;
int path_uart;
};
enum max14577_muic_cable_group {
MAX14577_CABLE_GROUP_ADC = 0,
MAX14577_CABLE_GROUP_CHG,
};
/**
* struct max14577_muic_irq
* @irq: the index of irq list of MUIC device.
* @name: the name of irq.
* @virq: the virtual irq to use irq domain
*/
struct max14577_muic_irq {
unsigned int irq;
const char *name;
unsigned int virq;
};
static struct max14577_muic_irq muic_irqs[] = {
{ MAX14577_IRQ_INT1_ADC, "muic-ADC" },
{ MAX14577_IRQ_INT1_ADCLOW, "muic-ADCLOW" },
{ MAX14577_IRQ_INT1_ADCERR, "muic-ADCError" },
{ MAX14577_IRQ_INT2_CHGTYP, "muic-CHGTYP" },
{ MAX14577_IRQ_INT2_CHGDETRUN, "muic-CHGDETRUN" },
{ MAX14577_IRQ_INT2_DCDTMR, "muic-DCDTMR" },
{ MAX14577_IRQ_INT2_DBCHG, "muic-DBCHG" },
{ MAX14577_IRQ_INT2_VBVOLT, "muic-VBVOLT" },
};
/* Define supported accessory type */
enum max14577_muic_acc_type {
MAX14577_MUIC_ADC_GROUND = 0x0,
MAX14577_MUIC_ADC_SEND_END_BUTTON,
MAX14577_MUIC_ADC_REMOTE_S1_BUTTON,
MAX14577_MUIC_ADC_REMOTE_S2_BUTTON,
MAX14577_MUIC_ADC_REMOTE_S3_BUTTON,
MAX14577_MUIC_ADC_REMOTE_S4_BUTTON,
MAX14577_MUIC_ADC_REMOTE_S5_BUTTON,
MAX14577_MUIC_ADC_REMOTE_S6_BUTTON,
MAX14577_MUIC_ADC_REMOTE_S7_BUTTON,
MAX14577_MUIC_ADC_REMOTE_S8_BUTTON,
MAX14577_MUIC_ADC_REMOTE_S9_BUTTON,
MAX14577_MUIC_ADC_REMOTE_S10_BUTTON,
MAX14577_MUIC_ADC_REMOTE_S11_BUTTON,
MAX14577_MUIC_ADC_REMOTE_S12_BUTTON,
MAX14577_MUIC_ADC_RESERVED_ACC_1,
MAX14577_MUIC_ADC_RESERVED_ACC_2,
MAX14577_MUIC_ADC_RESERVED_ACC_3,
MAX14577_MUIC_ADC_RESERVED_ACC_4,
MAX14577_MUIC_ADC_RESERVED_ACC_5,
MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE2,
MAX14577_MUIC_ADC_PHONE_POWERED_DEV,
MAX14577_MUIC_ADC_TTY_CONVERTER,
MAX14577_MUIC_ADC_UART_CABLE,
MAX14577_MUIC_ADC_CEA936A_TYPE1_CHG,
MAX14577_MUIC_ADC_FACTORY_MODE_USB_OFF,
MAX14577_MUIC_ADC_FACTORY_MODE_USB_ON,
MAX14577_MUIC_ADC_AV_CABLE_NOLOAD,
MAX14577_MUIC_ADC_CEA936A_TYPE2_CHG,
MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF,
MAX14577_MUIC_ADC_FACTORY_MODE_UART_ON,
MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE1, /* with Remote and Simple Ctrl */
MAX14577_MUIC_ADC_OPEN,
};
/* max14577 MUIC device support below list of accessories(external connector) */
enum {
EXTCON_CABLE_USB = 0,
EXTCON_CABLE_TA,
EXTCON_CABLE_FAST_CHARGER,
EXTCON_CABLE_SLOW_CHARGER,
EXTCON_CABLE_CHARGE_DOWNSTREAM,
EXTCON_CABLE_JIG_USB_ON,
EXTCON_CABLE_JIG_USB_OFF,
EXTCON_CABLE_JIG_UART_OFF,
EXTCON_CABLE_JIG_UART_ON,
_EXTCON_CABLE_NUM,
};
static const char *max14577_extcon_cable[] = {
[EXTCON_CABLE_USB] = "USB",
[EXTCON_CABLE_TA] = "TA",
[EXTCON_CABLE_FAST_CHARGER] = "Fast-charger",
[EXTCON_CABLE_SLOW_CHARGER] = "Slow-charger",
[EXTCON_CABLE_CHARGE_DOWNSTREAM] = "Charge-downstream",
[EXTCON_CABLE_JIG_USB_ON] = "JIG-USB-ON",
[EXTCON_CABLE_JIG_USB_OFF] = "JIG-USB-OFF",
[EXTCON_CABLE_JIG_UART_OFF] = "JIG-UART-OFF",
[EXTCON_CABLE_JIG_UART_ON] = "JIG-UART-ON",
NULL,
};
/*
* max14577_muic_set_debounce_time - Set the debounce time of ADC
* @info: the instance including private data of max14577 MUIC
* @time: the debounce time of ADC
*/
static int max14577_muic_set_debounce_time(struct max14577_muic_info *info,
enum max14577_muic_adc_debounce_time time)
{
u8 ret;
switch (time) {
case ADC_DEBOUNCE_TIME_5MS:
case ADC_DEBOUNCE_TIME_10MS:
case ADC_DEBOUNCE_TIME_25MS:
case ADC_DEBOUNCE_TIME_38_62MS:
ret = max14577_update_reg(info->max14577->regmap,
MAX14577_MUIC_REG_CONTROL3,
CTRL3_ADCDBSET_MASK,
time << CTRL3_ADCDBSET_SHIFT);
if (ret) {
dev_err(info->dev, "failed to set ADC debounce time\n");
return ret;
}
break;
default:
dev_err(info->dev, "invalid ADC debounce time\n");
return -EINVAL;
}
return 0;
};
/*
* max14577_muic_set_path - Set hardware line according to attached cable
* @info: the instance including private data of max14577 MUIC
* @value: the path according to attached cable
* @attached: the state of cable (true:attached, false:detached)
*
* The max14577 MUIC device share outside H/W line among a varity of cables
* so, this function set internal path of H/W line according to the type of
* attached cable.
*/
static int max14577_muic_set_path(struct max14577_muic_info *info,
u8 val, bool attached)
{
int ret = 0;
u8 ctrl1, ctrl2 = 0;
/* Set open state to path before changing hw path */
ret = max14577_update_reg(info->max14577->regmap,
MAX14577_MUIC_REG_CONTROL1,
CLEAR_IDBEN_MICEN_MASK, CTRL1_SW_OPEN);
if (ret < 0) {
dev_err(info->dev, "failed to update MUIC register\n");
return ret;
}
if (attached)
ctrl1 = val;
else
ctrl1 = CTRL1_SW_OPEN;
ret = max14577_update_reg(info->max14577->regmap,
MAX14577_MUIC_REG_CONTROL1,
CLEAR_IDBEN_MICEN_MASK, ctrl1);
if (ret < 0) {
dev_err(info->dev, "failed to update MUIC register\n");
return ret;
}
if (attached)
ctrl2 |= CTRL2_CPEN_MASK; /* LowPwr=0, CPEn=1 */
else
ctrl2 |= CTRL2_LOWPWR_MASK; /* LowPwr=1, CPEn=0 */
ret = max14577_update_reg(info->max14577->regmap,
MAX14577_REG_CONTROL2,
CTRL2_LOWPWR_MASK | CTRL2_CPEN_MASK, ctrl2);
if (ret < 0) {
dev_err(info->dev, "failed to update MUIC register\n");
return ret;
}
dev_dbg(info->dev,
"CONTROL1 : 0x%02x, CONTROL2 : 0x%02x, state : %s\n",
ctrl1, ctrl2, attached ? "attached" : "detached");
return 0;
}
/*
* max14577_muic_get_cable_type - Return cable type and check cable state
* @info: the instance including private data of max14577 MUIC
* @group: the path according to attached cable
* @attached: store cable state and return
*
* This function check the cable state either attached or detached,
* and then divide precise type of cable according to cable group.
* - max14577_CABLE_GROUP_ADC
* - max14577_CABLE_GROUP_CHG
*/
static int max14577_muic_get_cable_type(struct max14577_muic_info *info,
enum max14577_muic_cable_group group, bool *attached)
{
int cable_type = 0;
int adc;
int chg_type;
switch (group) {
case MAX14577_CABLE_GROUP_ADC:
/*
* Read ADC value to check cable type and decide cable state
* according to cable type
*/
adc = info->status[MAX14577_MUIC_STATUS1] & STATUS1_ADC_MASK;
adc >>= STATUS1_ADC_SHIFT;
/*
* Check current cable state/cable type and store cable type
* (info->prev_cable_type) for handling cable when cable is
* detached.
*/
if (adc == MAX14577_MUIC_ADC_OPEN) {
*attached = false;
cable_type = info->prev_cable_type;
info->prev_cable_type = MAX14577_MUIC_ADC_OPEN;
} else {
*attached = true;
cable_type = info->prev_cable_type = adc;
}
break;
case MAX14577_CABLE_GROUP_CHG:
/*
* Read charger type to check cable type and decide cable state
* according to type of charger cable.
*/
chg_type = info->status[MAX14577_MUIC_STATUS2] &
STATUS2_CHGTYP_MASK;
chg_type >>= STATUS2_CHGTYP_SHIFT;
if (chg_type == MAX14577_CHARGER_TYPE_NONE) {
*attached = false;
cable_type = info->prev_chg_type;
info->prev_chg_type = MAX14577_CHARGER_TYPE_NONE;
} else {
*attached = true;
/*
* Check current cable state/cable type and store cable
* type(info->prev_chg_type) for handling cable when
* charger cable is detached.
*/
cable_type = info->prev_chg_type = chg_type;
}
break;
default:
dev_err(info->dev, "Unknown cable group (%d)\n", group);
cable_type = -EINVAL;
break;
}
return cable_type;
}
static int max14577_muic_jig_handler(struct max14577_muic_info *info,
int cable_type, bool attached)
{
char cable_name[32];
int ret = 0;
u8 path = CTRL1_SW_OPEN;
dev_dbg(info->dev,
"external connector is %s (adc:0x%02x)\n",
attached ? "attached" : "detached", cable_type);
switch (cable_type) {
case MAX14577_MUIC_ADC_FACTORY_MODE_USB_OFF: /* ADC_JIG_USB_OFF */
/* PATH:AP_USB */
strcpy(cable_name, "JIG-USB-OFF");
path = CTRL1_SW_USB;
break;
case MAX14577_MUIC_ADC_FACTORY_MODE_USB_ON: /* ADC_JIG_USB_ON */
/* PATH:AP_USB */
strcpy(cable_name, "JIG-USB-ON");
path = CTRL1_SW_USB;
break;
case MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF: /* ADC_JIG_UART_OFF */
/* PATH:AP_UART */
strcpy(cable_name, "JIG-UART-OFF");
path = CTRL1_SW_UART;
break;
default:
dev_err(info->dev, "failed to detect %s jig cable\n",
attached ? "attached" : "detached");
return -EINVAL;
}
ret = max14577_muic_set_path(info, path, attached);
if (ret < 0)
return ret;
extcon_set_cable_state(info->edev, cable_name, attached);
return 0;
}
static int max14577_muic_adc_handler(struct max14577_muic_info *info)
{
int cable_type;
bool attached;
int ret = 0;
/* Check accessory state which is either detached or attached */
cable_type = max14577_muic_get_cable_type(info,
MAX14577_CABLE_GROUP_ADC, &attached);
dev_dbg(info->dev,
"external connector is %s (adc:0x%02x, prev_adc:0x%x)\n",
attached ? "attached" : "detached", cable_type,
info->prev_cable_type);
switch (cable_type) {
case MAX14577_MUIC_ADC_FACTORY_MODE_USB_OFF:
case MAX14577_MUIC_ADC_FACTORY_MODE_USB_ON:
case MAX14577_MUIC_ADC_FACTORY_MODE_UART_OFF:
/* JIG */
ret = max14577_muic_jig_handler(info, cable_type, attached);
if (ret < 0)
return ret;
break;
case MAX14577_MUIC_ADC_GROUND:
case MAX14577_MUIC_ADC_SEND_END_BUTTON:
case MAX14577_MUIC_ADC_REMOTE_S1_BUTTON:
case MAX14577_MUIC_ADC_REMOTE_S2_BUTTON:
case MAX14577_MUIC_ADC_REMOTE_S3_BUTTON:
case MAX14577_MUIC_ADC_REMOTE_S4_BUTTON:
case MAX14577_MUIC_ADC_REMOTE_S5_BUTTON:
case MAX14577_MUIC_ADC_REMOTE_S6_BUTTON:
case MAX14577_MUIC_ADC_REMOTE_S7_BUTTON:
case MAX14577_MUIC_ADC_REMOTE_S8_BUTTON:
case MAX14577_MUIC_ADC_REMOTE_S9_BUTTON:
case MAX14577_MUIC_ADC_REMOTE_S10_BUTTON:
case MAX14577_MUIC_ADC_REMOTE_S11_BUTTON:
case MAX14577_MUIC_ADC_REMOTE_S12_BUTTON:
case MAX14577_MUIC_ADC_RESERVED_ACC_1:
case MAX14577_MUIC_ADC_RESERVED_ACC_2:
case MAX14577_MUIC_ADC_RESERVED_ACC_3:
case MAX14577_MUIC_ADC_RESERVED_ACC_4:
case MAX14577_MUIC_ADC_RESERVED_ACC_5:
case MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE2:
case MAX14577_MUIC_ADC_PHONE_POWERED_DEV:
case MAX14577_MUIC_ADC_TTY_CONVERTER:
case MAX14577_MUIC_ADC_UART_CABLE:
case MAX14577_MUIC_ADC_CEA936A_TYPE1_CHG:
case MAX14577_MUIC_ADC_AV_CABLE_NOLOAD:
case MAX14577_MUIC_ADC_CEA936A_TYPE2_CHG:
case MAX14577_MUIC_ADC_FACTORY_MODE_UART_ON:
case MAX14577_MUIC_ADC_AUDIO_DEVICE_TYPE1:
/*
* This accessory isn't used in general case if it is specially
* needed to detect additional accessory, should implement
* proper operation when this accessory is attached/detached.
*/
dev_info(info->dev,
"accessory is %s but it isn't used (adc:0x%x)\n",
attached ? "attached" : "detached", cable_type);
return -EAGAIN;
default:
dev_err(info->dev,
"failed to detect %s accessory (adc:0x%x)\n",
attached ? "attached" : "detached", cable_type);
return -EINVAL;
}
return 0;
}
static int max14577_muic_chg_handler(struct max14577_muic_info *info)
{
int chg_type;
bool attached;
int ret = 0;
chg_type = max14577_muic_get_cable_type(info,
MAX14577_CABLE_GROUP_CHG, &attached);
dev_dbg(info->dev,
"external connector is %s(chg_type:0x%x, prev_chg_type:0x%x)\n",
attached ? "attached" : "detached",
chg_type, info->prev_chg_type);
switch (chg_type) {
case MAX14577_CHARGER_TYPE_USB:
/* PATH:AP_USB */
ret = max14577_muic_set_path(info, info->path_usb, attached);
if (ret < 0)
return ret;
extcon_set_cable_state(info->edev, "USB", attached);
break;
case MAX14577_CHARGER_TYPE_DEDICATED_CHG:
extcon_set_cable_state(info->edev, "TA", attached);
break;
case MAX14577_CHARGER_TYPE_DOWNSTREAM_PORT:
extcon_set_cable_state(info->edev,
"Charge-downstream", attached);
break;
case MAX14577_CHARGER_TYPE_SPECIAL_500MA:
extcon_set_cable_state(info->edev, "Slow-charger", attached);
break;
case MAX14577_CHARGER_TYPE_SPECIAL_1A:
extcon_set_cable_state(info->edev, "Fast-charger", attached);
break;
case MAX14577_CHARGER_TYPE_NONE:
case MAX14577_CHARGER_TYPE_DEAD_BATTERY:
break;
default:
dev_err(info->dev,
"failed to detect %s accessory (chg_type:0x%x)\n",
attached ? "attached" : "detached", chg_type);
return -EINVAL;
}
return 0;
}
static void max14577_muic_irq_work(struct work_struct *work)
{
struct max14577_muic_info *info = container_of(work,
struct max14577_muic_info, irq_work);
int ret = 0;
if (!info->edev)
return;
mutex_lock(&info->mutex);
ret = max14577_bulk_read(info->max14577->regmap,
MAX14577_MUIC_REG_STATUS1, info->status, 2);
if (ret) {
dev_err(info->dev, "failed to read MUIC register\n");
mutex_unlock(&info->mutex);
return;
}
if (info->irq_adc) {
ret = max14577_muic_adc_handler(info);
info->irq_adc = false;
}
if (info->irq_chg) {
ret = max14577_muic_chg_handler(info);
info->irq_chg = false;
}
if (ret < 0)
dev_err(info->dev, "failed to handle MUIC interrupt\n");
mutex_unlock(&info->mutex);
return;
}
static irqreturn_t max14577_muic_irq_handler(int irq, void *data)
{
struct max14577_muic_info *info = data;
int i, irq_type = -1;
/*
* We may be called multiple times for different nested IRQ-s.
* Including changes in INT1_ADC and INT2_CGHTYP at once.
* However we only need to know whether it was ADC, charger
* or both interrupts so decode IRQ and turn on proper flags.
*/
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++)
if (irq == muic_irqs[i].virq)
irq_type = muic_irqs[i].irq;
switch (irq_type) {
case MAX14577_IRQ_INT1_ADC:
case MAX14577_IRQ_INT1_ADCLOW:
case MAX14577_IRQ_INT1_ADCERR:
/* Handle all of accessory except for
type of charger accessory */
info->irq_adc = true;
break;
case MAX14577_IRQ_INT2_CHGTYP:
case MAX14577_IRQ_INT2_CHGDETRUN:
case MAX14577_IRQ_INT2_DCDTMR:
case MAX14577_IRQ_INT2_DBCHG:
case MAX14577_IRQ_INT2_VBVOLT:
/* Handle charger accessory */
info->irq_chg = true;
break;
default:
dev_err(info->dev, "muic interrupt: irq %d occurred, skipped\n",
irq_type);
return IRQ_HANDLED;
}
schedule_work(&info->irq_work);
return IRQ_HANDLED;
}
static int max14577_muic_detect_accessory(struct max14577_muic_info *info)
{
int ret = 0;
int adc;
int chg_type;
bool attached;
mutex_lock(&info->mutex);
/* Read STATUSx register to detect accessory */
ret = max14577_bulk_read(info->max14577->regmap,
MAX14577_MUIC_REG_STATUS1, info->status, 2);
if (ret) {
dev_err(info->dev, "failed to read MUIC register\n");
mutex_unlock(&info->mutex);
return ret;
}
adc = max14577_muic_get_cable_type(info, MAX14577_CABLE_GROUP_ADC,
&attached);
if (attached && adc != MAX14577_MUIC_ADC_OPEN) {
ret = max14577_muic_adc_handler(info);
if (ret < 0) {
dev_err(info->dev, "Cannot detect accessory\n");
mutex_unlock(&info->mutex);
return ret;
}
}
chg_type = max14577_muic_get_cable_type(info, MAX14577_CABLE_GROUP_CHG,
&attached);
if (attached && chg_type != MAX14577_CHARGER_TYPE_NONE) {
ret = max14577_muic_chg_handler(info);
if (ret < 0) {
dev_err(info->dev, "Cannot detect charger accessory\n");
mutex_unlock(&info->mutex);
return ret;
}
}
mutex_unlock(&info->mutex);
return 0;
}
static void max14577_muic_detect_cable_wq(struct work_struct *work)
{
struct max14577_muic_info *info = container_of(to_delayed_work(work),
struct max14577_muic_info, wq_detcable);
max14577_muic_detect_accessory(info);
}
static int max14577_muic_probe(struct platform_device *pdev)
{
struct max14577 *max14577 = dev_get_drvdata(pdev->dev.parent);
struct max14577_muic_info *info;
int delay_jiffies;
int ret;
int i;
u8 id;
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) {
dev_err(&pdev->dev, "failed to allocate memory\n");
return -ENOMEM;
}
info->dev = &pdev->dev;
info->max14577 = max14577;
platform_set_drvdata(pdev, info);
mutex_init(&info->mutex);
INIT_WORK(&info->irq_work, max14577_muic_irq_work);
/* Support irq domain for max14577 MUIC device */
for (i = 0; i < ARRAY_SIZE(muic_irqs); i++) {
struct max14577_muic_irq *muic_irq = &muic_irqs[i];
unsigned int virq = 0;
virq = regmap_irq_get_virq(max14577->irq_data, muic_irq->irq);
if (!virq)
return -EINVAL;
muic_irq->virq = virq;
ret = devm_request_threaded_irq(&pdev->dev, virq, NULL,
max14577_muic_irq_handler,
IRQF_NO_SUSPEND,
muic_irq->name, info);
if (ret) {
dev_err(&pdev->dev,
"failed: irq request (IRQ: %d,"
" error :%d)\n",
muic_irq->irq, ret);
return ret;
}
}
/* Initialize extcon device */
info->edev = devm_kzalloc(&pdev->dev, sizeof(*info->edev), GFP_KERNEL);
if (!info->edev) {
dev_err(&pdev->dev, "failed to allocate memory for extcon\n");
return -ENOMEM;
}
info->edev->name = DEV_NAME;
info->edev->supported_cable = max14577_extcon_cable;
ret = extcon_dev_register(info->edev);
if (ret) {
dev_err(&pdev->dev, "failed to register extcon device\n");
return ret;
}
/* Default h/w line path */
info->path_usb = CTRL1_SW_USB;
info->path_uart = CTRL1_SW_UART;
delay_jiffies = msecs_to_jiffies(DELAY_MS_DEFAULT);
/* Set initial path for UART */
max14577_muic_set_path(info, info->path_uart, true);
/* Check revision number of MUIC device*/
ret = max14577_read_reg(info->max14577->regmap,
MAX14577_REG_DEVICEID, &id);
if (ret < 0) {
dev_err(&pdev->dev, "failed to read revision number\n");
goto err_extcon;
}
dev_info(info->dev, "device ID : 0x%x\n", id);
/* Set ADC debounce time */
max14577_muic_set_debounce_time(info, ADC_DEBOUNCE_TIME_25MS);
/*
* Detect accessory after completing the initialization of platform
*
* - Use delayed workqueue to detect cable state and then
* notify cable state to notifiee/platform through uevent.
* After completing the booting of platform, the extcon provider
* driver should notify cable state to upper layer.
*/
INIT_DELAYED_WORK(&info->wq_detcable, max14577_muic_detect_cable_wq);
ret = queue_delayed_work(system_power_efficient_wq, &info->wq_detcable,
delay_jiffies);
if (ret < 0) {
dev_err(&pdev->dev,
"failed to schedule delayed work for cable detect\n");
goto err_extcon;
}
return ret;
err_extcon:
extcon_dev_unregister(info->edev);
return ret;
}
static int max14577_muic_remove(struct platform_device *pdev)
{
struct max14577_muic_info *info = platform_get_drvdata(pdev);
cancel_work_sync(&info->irq_work);
extcon_dev_unregister(info->edev);
return 0;
}
static struct platform_driver max14577_muic_driver = {
.driver = {
.name = DEV_NAME,
.owner = THIS_MODULE,
},
.probe = max14577_muic_probe,
.remove = max14577_muic_remove,
};
module_platform_driver(max14577_muic_driver);
MODULE_DESCRIPTION("MAXIM 14577 Extcon driver");
MODULE_AUTHOR("Chanwoo Choi <cw00.choi@samsung.com>");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:extcon-max14577");

View File

@ -78,20 +78,24 @@ static irqreturn_t palmas_vbus_irq_handler(int irq, void *_palmas_usb)
static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
{
unsigned int set;
unsigned int set, id_src;
struct palmas_usb *palmas_usb = _palmas_usb;
palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_LATCH_SET, &set);
palmas_read(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_SRC, &id_src);
if (set & PALMAS_USB_ID_INT_SRC_ID_GND) {
if ((set & PALMAS_USB_ID_INT_SRC_ID_GND) &&
(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_GND);
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true);
dev_info(palmas_usb->dev, "USB-HOST cable is attached\n");
} else if (set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) {
} else if ((set & PALMAS_USB_ID_INT_SRC_ID_FLOAT) &&
(id_src & PALMAS_USB_ID_INT_SRC_ID_FLOAT)) {
palmas_write(palmas_usb->palmas, PALMAS_USB_OTG_BASE,
PALMAS_USB_ID_INT_LATCH_CLR,
PALMAS_USB_ID_INT_EN_HI_CLR_ID_FLOAT);
@ -103,6 +107,11 @@ static irqreturn_t palmas_id_irq_handler(int irq, void *_palmas_usb)
palmas_usb->linkstat = PALMAS_USB_STATE_DISCONNECT;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", false);
dev_info(palmas_usb->dev, "USB-HOST cable is detached\n");
} else if ((palmas_usb->linkstat == PALMAS_USB_STATE_DISCONNECT) &&
(id_src & PALMAS_USB_ID_INT_SRC_ID_GND)) {
palmas_usb->linkstat = PALMAS_USB_STATE_ID;
extcon_set_cable_state(&palmas_usb->edev, "USB-HOST", true);
dev_info(palmas_usb->dev, " USB-HOST cable is attached\n");
}
return IRQ_HANDLED;
@ -269,7 +278,9 @@ static const struct dev_pm_ops palmas_pm_ops = {
static struct of_device_id of_palmas_match_tbl[] = {
{ .compatible = "ti,palmas-usb", },
{ .compatible = "ti,palmas-usb-vid", },
{ .compatible = "ti,twl6035-usb", },
{ .compatible = "ti,twl6035-usb-vid", },
{ /* end */ }
};

View File

@ -301,7 +301,7 @@ err:
return -ENOMEM;
}
void hv_synic_free_cpu(int cpu)
static void hv_synic_free_cpu(int cpu)
{
kfree(hv_context.event_dpc[cpu]);
if (hv_context.synic_event_page[cpu])

View File

@ -525,4 +525,5 @@ source "drivers/misc/altera-stapl/Kconfig"
source "drivers/misc/mei/Kconfig"
source "drivers/misc/vmw_vmci/Kconfig"
source "drivers/misc/mic/Kconfig"
source "drivers/misc/genwqe/Kconfig"
endmenu

View File

@ -53,3 +53,4 @@ obj-$(CONFIG_VMWARE_VMCI) += vmw_vmci/
obj-$(CONFIG_LATTICE_ECP3_CONFIG) += lattice-ecp3-config.o
obj-$(CONFIG_SRAM) += sram.o
obj-y += mic/
obj-$(CONFIG_GENWQE) += genwqe/

View File

@ -641,7 +641,7 @@ static const struct attribute_group ad525x_group_commands = {
.attrs = ad525x_attributes_commands,
};
int ad_dpot_add_files(struct device *dev,
static int ad_dpot_add_files(struct device *dev,
unsigned features, unsigned rdac)
{
int err = sysfs_create_file(&dev->kobj,
@ -666,7 +666,7 @@ int ad_dpot_add_files(struct device *dev,
return err;
}
inline void ad_dpot_remove_files(struct device *dev,
static inline void ad_dpot_remove_files(struct device *dev,
unsigned features, unsigned rdac)
{
sysfs_remove_file(&dev->kobj,

View File

@ -49,7 +49,7 @@ static int bmp085_i2c_probe(struct i2c_client *client,
return err;
}
return bmp085_probe(&client->dev, regmap);
return bmp085_probe(&client->dev, regmap, client->irq);
}
static int bmp085_i2c_remove(struct i2c_client *client)

View File

@ -41,7 +41,7 @@ static int bmp085_spi_probe(struct spi_device *client)
return err;
}
return bmp085_probe(&client->dev, regmap);
return bmp085_probe(&client->dev, regmap, client->irq);
}
static int bmp085_spi_remove(struct spi_device *client)

View File

@ -49,9 +49,11 @@
#include <linux/device.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/delay.h>
#include <linux/of.h>
#include "bmp085.h"
#include <linux/interrupt.h>
#include <linux/completion.h>
#include <linux/gpio.h>
#define BMP085_CHIP_ID 0x55
#define BMP085_CALIBRATION_DATA_START 0xAA
@ -84,8 +86,19 @@ struct bmp085_data {
unsigned long last_temp_measurement;
u8 chip_id;
s32 b6; /* calculated temperature correction coefficient */
int irq;
struct completion done;
};
static irqreturn_t bmp085_eoc_isr(int irq, void *devid)
{
struct bmp085_data *data = devid;
complete(&data->done);
return IRQ_HANDLED;
}
static s32 bmp085_read_calibration_data(struct bmp085_data *data)
{
u16 tmp[BMP085_CALIBRATION_DATA_LENGTH];
@ -116,6 +129,9 @@ static s32 bmp085_update_raw_temperature(struct bmp085_data *data)
s32 status;
mutex_lock(&data->lock);
init_completion(&data->done);
status = regmap_write(data->regmap, BMP085_CTRL_REG,
BMP085_TEMP_MEASUREMENT);
if (status < 0) {
@ -123,7 +139,8 @@ static s32 bmp085_update_raw_temperature(struct bmp085_data *data)
"Error while requesting temperature measurement.\n");
goto exit;
}
msleep(BMP085_TEMP_CONVERSION_TIME);
wait_for_completion_timeout(&data->done, 1 + msecs_to_jiffies(
BMP085_TEMP_CONVERSION_TIME));
status = regmap_bulk_read(data->regmap, BMP085_CONVERSION_REGISTER_MSB,
&tmp, sizeof(tmp));
@ -147,6 +164,9 @@ static s32 bmp085_update_raw_pressure(struct bmp085_data *data)
s32 status;
mutex_lock(&data->lock);
init_completion(&data->done);
status = regmap_write(data->regmap, BMP085_CTRL_REG,
BMP085_PRESSURE_MEASUREMENT +
(data->oversampling_setting << 6));
@ -157,8 +177,8 @@ static s32 bmp085_update_raw_pressure(struct bmp085_data *data)
}
/* wait for the end of conversion */
msleep(2+(3 << data->oversampling_setting));
wait_for_completion_timeout(&data->done, 1 + msecs_to_jiffies(
2+(3 << data->oversampling_setting)));
/* copy data into a u32 (4 bytes), but skip the first byte. */
status = regmap_bulk_read(data->regmap, BMP085_CONVERSION_REGISTER_MSB,
((u8 *)&tmp)+1, 3);
@ -420,7 +440,7 @@ struct regmap_config bmp085_regmap_config = {
};
EXPORT_SYMBOL_GPL(bmp085_regmap_config);
int bmp085_probe(struct device *dev, struct regmap *regmap)
int bmp085_probe(struct device *dev, struct regmap *regmap, int irq)
{
struct bmp085_data *data;
int err = 0;
@ -434,6 +454,15 @@ int bmp085_probe(struct device *dev, struct regmap *regmap)
dev_set_drvdata(dev, data);
data->dev = dev;
data->regmap = regmap;
data->irq = irq;
if (data->irq > 0) {
err = devm_request_irq(dev, data->irq, bmp085_eoc_isr,
IRQF_TRIGGER_RISING, "bmp085",
data);
if (err < 0)
goto exit_free;
}
/* Initialize the BMP085 chip */
err = bmp085_init_client(data);

View File

@ -26,7 +26,7 @@
extern struct regmap_config bmp085_regmap_config;
int bmp085_probe(struct device *dev, struct regmap *regmap);
int bmp085_probe(struct device *dev, struct regmap *regmap, int irq);
int bmp085_remove(struct device *dev);
int bmp085_detect(struct device *dev);

View File

@ -378,7 +378,6 @@ static int eeprom_93xx46_remove(struct spi_device *spi)
device_remove_file(&spi->dev, &dev_attr_erase);
sysfs_remove_bin_file(&spi->dev.kobj, &edev->bin);
spi_set_drvdata(spi, NULL);
kfree(edev);
return 0;
}

View File

@ -0,0 +1,13 @@
#
# IBM Accelerator Family 'GenWQE'
#
menuconfig GENWQE
tristate "GenWQE PCIe Accelerator"
depends on PCI && 64BIT
select CRC_ITU_T
default n
help
Enables PCIe card driver for IBM GenWQE accelerators.
The user-space interface is described in
include/linux/genwqe/genwqe_card.h.

View File

@ -0,0 +1,7 @@
#
# Makefile for GenWQE driver
#
obj-$(CONFIG_GENWQE) := genwqe_card.o
genwqe_card-objs := card_base.o card_dev.o card_ddcb.o card_sysfs.o \
card_debugfs.o card_utils.o

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,557 @@
#ifndef __CARD_BASE_H__
#define __CARD_BASE_H__
/**
* IBM Accelerator Family 'GenWQE'
*
* (C) Copyright IBM Corp. 2013
*
* Author: Frank Haverkamp <haver@linux.vnet.ibm.com>
* Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com>
* Author: Michael Jung <mijung@de.ibm.com>
* Author: Michael Ruettger <michael@ibmra.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* Interfaces within the GenWQE module. Defines genwqe_card and
* ddcb_queue as well as ddcb_requ.
*/
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/cdev.h>
#include <linux/stringify.h>
#include <linux/pci.h>
#include <linux/semaphore.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/version.h>
#include <linux/debugfs.h>
#include <linux/slab.h>
#include <linux/genwqe/genwqe_card.h>
#include "genwqe_driver.h"
#define GENWQE_MSI_IRQS 4 /* Just one supported, no MSIx */
#define GENWQE_FLAG_MSI_ENABLED (1 << 0)
#define GENWQE_MAX_VFS 15 /* maximum 15 VFs are possible */
#define GENWQE_MAX_FUNCS 16 /* 1 PF and 15 VFs */
#define GENWQE_CARD_NO_MAX (16 * GENWQE_MAX_FUNCS)
/* Compile parameters, some of them appear in debugfs for later adjustment */
#define genwqe_ddcb_max 32 /* DDCBs on the work-queue */
#define genwqe_polling_enabled 0 /* in case of irqs not working */
#define genwqe_ddcb_software_timeout 10 /* timeout per DDCB in seconds */
#define genwqe_kill_timeout 8 /* time until process gets killed */
#define genwqe_vf_jobtimeout_msec 250 /* 250 msec */
#define genwqe_pf_jobtimeout_msec 8000 /* 8 sec should be ok */
#define genwqe_health_check_interval 4 /* <= 0: disabled */
/* Sysfs attribute groups used when we create the genwqe device */
extern const struct attribute_group *genwqe_attribute_groups[];
/*
* Config space for Genwqe5 A7:
* 00:[14 10 4b 04]40 00 10 00[00 00 00 12]00 00 00 00
* 10: 0c 00 00 f0 07 3c 00 00 00 00 00 00 00 00 00 00
* 20: 00 00 00 00 00 00 00 00 00 00 00 00[14 10 4b 04]
* 30: 00 00 00 00 50 00 00 00 00 00 00 00 00 00 00 00
*/
#define PCI_DEVICE_GENWQE 0x044b /* Genwqe DeviceID */
#define PCI_SUBSYSTEM_ID_GENWQE5 0x035f /* Genwqe A5 Subsystem-ID */
#define PCI_SUBSYSTEM_ID_GENWQE5_NEW 0x044b /* Genwqe A5 Subsystem-ID */
#define PCI_CLASSCODE_GENWQE5 0x1200 /* UNKNOWN */
#define PCI_SUBVENDOR_ID_IBM_SRIOV 0x0000
#define PCI_SUBSYSTEM_ID_GENWQE5_SRIOV 0x0000 /* Genwqe A5 Subsystem-ID */
#define PCI_CLASSCODE_GENWQE5_SRIOV 0x1200 /* UNKNOWN */
#define GENWQE_SLU_ARCH_REQ 2 /* Required SLU architecture level */
/**
* struct genwqe_reg - Genwqe data dump functionality
*/
struct genwqe_reg {
u32 addr;
u32 idx;
u64 val;
};
/*
* enum genwqe_dbg_type - Specify chip unit to dump/debug
*/
enum genwqe_dbg_type {
GENWQE_DBG_UNIT0 = 0, /* captured before prev errs cleared */
GENWQE_DBG_UNIT1 = 1,
GENWQE_DBG_UNIT2 = 2,
GENWQE_DBG_UNIT3 = 3,
GENWQE_DBG_UNIT4 = 4,
GENWQE_DBG_UNIT5 = 5,
GENWQE_DBG_UNIT6 = 6,
GENWQE_DBG_UNIT7 = 7,
GENWQE_DBG_REGS = 8,
GENWQE_DBG_DMA = 9,
GENWQE_DBG_UNITS = 10, /* max number of possible debug units */
};
/* Software error injection to simulate card failures */
#define GENWQE_INJECT_HARDWARE_FAILURE 0x00000001 /* injects -1 reg reads */
#define GENWQE_INJECT_BUS_RESET_FAILURE 0x00000002 /* pci_bus_reset fail */
#define GENWQE_INJECT_GFIR_FATAL 0x00000004 /* GFIR = 0x0000ffff */
#define GENWQE_INJECT_GFIR_INFO 0x00000008 /* GFIR = 0xffff0000 */
/*
* Genwqe card description and management data.
*
* Error-handling in case of card malfunction
* ------------------------------------------
*
* If the card is detected to be defective the outside environment
* will cause the PCI layer to call deinit (the cleanup function for
* probe). This is the same effect like doing a unbind/bind operation
* on the card.
*
* The genwqe card driver implements a health checking thread which
* verifies the card function. If this detects a problem the cards
* device is being shutdown and restarted again, along with a reset of
* the card and queue.
*
* All functions accessing the card device return either -EIO or -ENODEV
* code to indicate the malfunction to the user. The user has to close
* the file descriptor and open a new one, once the card becomes
* available again.
*
* If the open file descriptor is setup to receive SIGIO, the signal is
* genereated for the application which has to provide a handler to
* react on it. If the application does not close the open
* file descriptor a SIGKILL is send to enforce freeing the cards
* resources.
*
* I did not find a different way to prevent kernel problems due to
* reference counters for the cards character devices getting out of
* sync. The character device deallocation does not block, even if
* there is still an open file descriptor pending. If this pending
* descriptor is closed, the data structures used by the character
* device is reinstantiated, which will lead to the reference counter
* dropping below the allowed values.
*
* Card recovery
* -------------
*
* To test the internal driver recovery the following command can be used:
* sudo sh -c 'echo 0xfffff > /sys/class/genwqe/genwqe0_card/err_inject'
*/
/**
* struct dma_mapping_type - Mapping type definition
*
* To avoid memcpying data arround we use user memory directly. To do
* this we need to pin/swap-in the memory and request a DMA address
* for it.
*/
enum dma_mapping_type {
GENWQE_MAPPING_RAW = 0, /* contignous memory buffer */
GENWQE_MAPPING_SGL_TEMP, /* sglist dynamically used */
GENWQE_MAPPING_SGL_PINNED, /* sglist used with pinning */
};
/**
* struct dma_mapping - Information about memory mappings done by the driver
*/
struct dma_mapping {
enum dma_mapping_type type;
void *u_vaddr; /* user-space vaddr/non-aligned */
void *k_vaddr; /* kernel-space vaddr/non-aligned */
dma_addr_t dma_addr; /* physical DMA address */
struct page **page_list; /* list of pages used by user buff */
dma_addr_t *dma_list; /* list of dma addresses per page */
unsigned int nr_pages; /* number of pages */
unsigned int size; /* size in bytes */
struct list_head card_list; /* list of usr_maps for card */
struct list_head pin_list; /* list of pinned memory for dev */
};
static inline void genwqe_mapping_init(struct dma_mapping *m,
enum dma_mapping_type type)
{
memset(m, 0, sizeof(*m));
m->type = type;
}
/**
* struct ddcb_queue - DDCB queue data
* @ddcb_max: Number of DDCBs on the queue
* @ddcb_next: Next free DDCB
* @ddcb_act: Next DDCB supposed to finish
* @ddcb_seq: Sequence number of last DDCB
* @ddcbs_in_flight: Currently enqueued DDCBs
* @ddcbs_completed: Number of already completed DDCBs
* @busy: Number of -EBUSY returns
* @ddcb_daddr: DMA address of first DDCB in the queue
* @ddcb_vaddr: Kernel virtual address of first DDCB in the queue
* @ddcb_req: Associated requests (one per DDCB)
* @ddcb_waitqs: Associated wait queues (one per DDCB)
* @ddcb_lock: Lock to protect queuing operations
* @ddcb_waitq: Wait on next DDCB finishing
*/
struct ddcb_queue {
int ddcb_max; /* amount of DDCBs */
int ddcb_next; /* next available DDCB num */
int ddcb_act; /* DDCB to be processed */
u16 ddcb_seq; /* slc seq num */
unsigned int ddcbs_in_flight; /* number of ddcbs in processing */
unsigned int ddcbs_completed;
unsigned int ddcbs_max_in_flight;
unsigned int busy; /* how many times -EBUSY? */
dma_addr_t ddcb_daddr; /* DMA address */
struct ddcb *ddcb_vaddr; /* kernel virtual addr for DDCBs */
struct ddcb_requ **ddcb_req; /* ddcb processing parameter */
wait_queue_head_t *ddcb_waitqs; /* waitqueue per ddcb */
spinlock_t ddcb_lock; /* exclusive access to queue */
wait_queue_head_t ddcb_waitq; /* wait for ddcb processing */
/* registers or the respective queue to be used */
u32 IO_QUEUE_CONFIG;
u32 IO_QUEUE_STATUS;
u32 IO_QUEUE_SEGMENT;
u32 IO_QUEUE_INITSQN;
u32 IO_QUEUE_WRAP;
u32 IO_QUEUE_OFFSET;
u32 IO_QUEUE_WTIME;
u32 IO_QUEUE_ERRCNTS;
u32 IO_QUEUE_LRW;
};
/*
* GFIR, SLU_UNITCFG, APP_UNITCFG
* 8 Units with FIR/FEC + 64 * 2ndary FIRS/FEC.
*/
#define GENWQE_FFDC_REGS (3 + (8 * (2 + 2 * 64)))
struct genwqe_ffdc {
unsigned int entries;
struct genwqe_reg *regs;
};
/**
* struct genwqe_dev - GenWQE device information
* @card_state: Card operation state, see above
* @ffdc: First Failure Data Capture buffers for each unit
* @card_thread: Working thread to operate the DDCB queue
* @card_waitq: Wait queue used in card_thread
* @queue: DDCB queue
* @health_thread: Card monitoring thread (only for PFs)
* @health_waitq: Wait queue used in health_thread
* @pci_dev: Associated PCI device (function)
* @mmio: Base address of 64-bit register space
* @mmio_len: Length of register area
* @file_lock: Lock to protect access to file_list
* @file_list: List of all processes with open GenWQE file descriptors
*
* This struct contains all information needed to communicate with a
* GenWQE card. It is initialized when a GenWQE device is found and
* destroyed when it goes away. It holds data to maintain the queue as
* well as data needed to feed the user interfaces.
*/
struct genwqe_dev {
enum genwqe_card_state card_state;
spinlock_t print_lock;
int card_idx; /* card index 0..CARD_NO_MAX-1 */
u64 flags; /* general flags */
/* FFDC data gathering */
struct genwqe_ffdc ffdc[GENWQE_DBG_UNITS];
/* DDCB workqueue */
struct task_struct *card_thread;
wait_queue_head_t queue_waitq;
struct ddcb_queue queue; /* genwqe DDCB queue */
unsigned int irqs_processed;
/* Card health checking thread */
struct task_struct *health_thread;
wait_queue_head_t health_waitq;
/* char device */
dev_t devnum_genwqe; /* major/minor num card */
struct class *class_genwqe; /* reference to class object */
struct device *dev; /* for device creation */
struct cdev cdev_genwqe; /* char device for card */
struct dentry *debugfs_root; /* debugfs card root directory */
struct dentry *debugfs_genwqe; /* debugfs driver root directory */
/* pci resources */
struct pci_dev *pci_dev; /* PCI device */
void __iomem *mmio; /* BAR-0 MMIO start */
unsigned long mmio_len;
u16 num_vfs;
u32 vf_jobtimeout_msec[GENWQE_MAX_VFS];
int is_privileged; /* access to all regs possible */
/* config regs which we need often */
u64 slu_unitcfg;
u64 app_unitcfg;
u64 softreset;
u64 err_inject;
u64 last_gfir;
char app_name[5];
spinlock_t file_lock; /* lock for open files */
struct list_head file_list; /* list of open files */
/* debugfs parameters */
int ddcb_software_timeout; /* wait until DDCB times out */
int skip_recovery; /* circumvention if recovery fails */
int kill_timeout; /* wait after sending SIGKILL */
};
/**
* enum genwqe_requ_state - State of a DDCB execution request
*/
enum genwqe_requ_state {
GENWQE_REQU_NEW = 0,
GENWQE_REQU_ENQUEUED = 1,
GENWQE_REQU_TAPPED = 2,
GENWQE_REQU_FINISHED = 3,
GENWQE_REQU_STATE_MAX,
};
/**
* struct ddcb_requ - Kernel internal representation of the DDCB request
* @cmd: User space representation of the DDCB execution request
*/
struct ddcb_requ {
/* kernel specific content */
enum genwqe_requ_state req_state; /* request status */
int num; /* ddcb_no for this request */
struct ddcb_queue *queue; /* associated queue */
struct dma_mapping dma_mappings[DDCB_FIXUPS];
struct sg_entry *sgl[DDCB_FIXUPS];
dma_addr_t sgl_dma_addr[DDCB_FIXUPS];
size_t sgl_size[DDCB_FIXUPS];
/* kernel/user shared content */
struct genwqe_ddcb_cmd cmd; /* ddcb_no for this request */
struct genwqe_debug_data debug_data;
};
/**
* struct genwqe_file - Information for open GenWQE devices
*/
struct genwqe_file {
struct genwqe_dev *cd;
struct genwqe_driver *client;
struct file *filp;
struct fasync_struct *async_queue;
struct task_struct *owner;
struct list_head list; /* entry in list of open files */
spinlock_t map_lock; /* lock for dma_mappings */
struct list_head map_list; /* list of dma_mappings */
spinlock_t pin_lock; /* lock for pinned memory */
struct list_head pin_list; /* list of pinned memory */
};
int genwqe_setup_service_layer(struct genwqe_dev *cd); /* for PF only */
int genwqe_finish_queue(struct genwqe_dev *cd);
int genwqe_release_service_layer(struct genwqe_dev *cd);
/**
* genwqe_get_slu_id() - Read Service Layer Unit Id
* Return: 0x00: Development code
* 0x01: SLC1 (old)
* 0x02: SLC2 (sept2012)
* 0x03: SLC2 (feb2013, generic driver)
*/
static inline int genwqe_get_slu_id(struct genwqe_dev *cd)
{
return (int)((cd->slu_unitcfg >> 32) & 0xff);
}
int genwqe_ddcbs_in_flight(struct genwqe_dev *cd);
u8 genwqe_card_type(struct genwqe_dev *cd);
int genwqe_card_reset(struct genwqe_dev *cd);
int genwqe_set_interrupt_capability(struct genwqe_dev *cd, int count);
void genwqe_reset_interrupt_capability(struct genwqe_dev *cd);
int genwqe_device_create(struct genwqe_dev *cd);
int genwqe_device_remove(struct genwqe_dev *cd);
/* debugfs */
int genwqe_init_debugfs(struct genwqe_dev *cd);
void genqwe_exit_debugfs(struct genwqe_dev *cd);
int genwqe_read_softreset(struct genwqe_dev *cd);
/* Hardware Circumventions */
int genwqe_recovery_on_fatal_gfir_required(struct genwqe_dev *cd);
int genwqe_flash_readback_fails(struct genwqe_dev *cd);
/**
* genwqe_write_vreg() - Write register in VF window
* @cd: genwqe device
* @reg: register address
* @val: value to write
* @func: 0: PF, 1: VF0, ..., 15: VF14
*/
int genwqe_write_vreg(struct genwqe_dev *cd, u32 reg, u64 val, int func);
/**
* genwqe_read_vreg() - Read register in VF window
* @cd: genwqe device
* @reg: register address
* @func: 0: PF, 1: VF0, ..., 15: VF14
*
* Return: content of the register
*/
u64 genwqe_read_vreg(struct genwqe_dev *cd, u32 reg, int func);
/* FFDC Buffer Management */
int genwqe_ffdc_buff_size(struct genwqe_dev *cd, int unit_id);
int genwqe_ffdc_buff_read(struct genwqe_dev *cd, int unit_id,
struct genwqe_reg *regs, unsigned int max_regs);
int genwqe_read_ffdc_regs(struct genwqe_dev *cd, struct genwqe_reg *regs,
unsigned int max_regs, int all);
int genwqe_ffdc_dump_dma(struct genwqe_dev *cd,
struct genwqe_reg *regs, unsigned int max_regs);
int genwqe_init_debug_data(struct genwqe_dev *cd,
struct genwqe_debug_data *d);
void genwqe_init_crc32(void);
int genwqe_read_app_id(struct genwqe_dev *cd, char *app_name, int len);
/* Memory allocation/deallocation; dma address handling */
int genwqe_user_vmap(struct genwqe_dev *cd, struct dma_mapping *m,
void *uaddr, unsigned long size,
struct ddcb_requ *req);
int genwqe_user_vunmap(struct genwqe_dev *cd, struct dma_mapping *m,
struct ddcb_requ *req);
struct sg_entry *genwqe_alloc_sgl(struct genwqe_dev *cd, int num_pages,
dma_addr_t *dma_addr, size_t *sgl_size);
void genwqe_free_sgl(struct genwqe_dev *cd, struct sg_entry *sg_list,
dma_addr_t dma_addr, size_t size);
int genwqe_setup_sgl(struct genwqe_dev *cd,
unsigned long offs,
unsigned long size,
struct sg_entry *sgl, /* genwqe sgl */
dma_addr_t dma_addr, size_t sgl_size,
dma_addr_t *dma_list, int page_offs, int num_pages);
int genwqe_check_sgl(struct genwqe_dev *cd, struct sg_entry *sg_list,
int size);
static inline bool dma_mapping_used(struct dma_mapping *m)
{
if (!m)
return 0;
return m->size != 0;
}
/**
* __genwqe_execute_ddcb() - Execute DDCB request with addr translation
*
* This function will do the address translation changes to the DDCBs
* according to the definitions required by the ATS field. It looks up
* the memory allocation buffer or does vmap/vunmap for the respective
* user-space buffers, inclusive page pinning and scatter gather list
* buildup and teardown.
*/
int __genwqe_execute_ddcb(struct genwqe_dev *cd,
struct genwqe_ddcb_cmd *cmd);
/**
* __genwqe_execute_raw_ddcb() - Execute DDCB request without addr translation
*
* This version will not do address translation or any modifcation of
* the DDCB data. It is used e.g. for the MoveFlash DDCB which is
* entirely prepared by the driver itself. That means the appropriate
* DMA addresses are already in the DDCB and do not need any
* modification.
*/
int __genwqe_execute_raw_ddcb(struct genwqe_dev *cd,
struct genwqe_ddcb_cmd *cmd);
int __genwqe_enqueue_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req);
int __genwqe_wait_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req);
int __genwqe_purge_ddcb(struct genwqe_dev *cd, struct ddcb_requ *req);
/* register access */
int __genwqe_writeq(struct genwqe_dev *cd, u64 byte_offs, u64 val);
u64 __genwqe_readq(struct genwqe_dev *cd, u64 byte_offs);
int __genwqe_writel(struct genwqe_dev *cd, u64 byte_offs, u32 val);
u32 __genwqe_readl(struct genwqe_dev *cd, u64 byte_offs);
void *__genwqe_alloc_consistent(struct genwqe_dev *cd, size_t size,
dma_addr_t *dma_handle);
void __genwqe_free_consistent(struct genwqe_dev *cd, size_t size,
void *vaddr, dma_addr_t dma_handle);
/* Base clock frequency in MHz */
int genwqe_base_clock_frequency(struct genwqe_dev *cd);
/* Before FFDC is captured the traps should be stopped. */
void genwqe_stop_traps(struct genwqe_dev *cd);
void genwqe_start_traps(struct genwqe_dev *cd);
/* Hardware circumvention */
bool genwqe_need_err_masking(struct genwqe_dev *cd);
/**
* genwqe_is_privileged() - Determine operation mode for PCI function
*
* On Intel with SRIOV support we see:
* PF: is_physfn = 1 is_virtfn = 0
* VF: is_physfn = 0 is_virtfn = 1
*
* On Systems with no SRIOV support _and_ virtualized systems we get:
* is_physfn = 0 is_virtfn = 0
*
* Other vendors have individual pci device ids to distinguish between
* virtual function drivers and physical function drivers. GenWQE
* unfortunately has just on pci device id for both, VFs and PF.
*
* The following code is used to distinguish if the card is running in
* privileged mode, either as true PF or in a virtualized system with
* full register access e.g. currently on PowerPC.
*
* if (pci_dev->is_virtfn)
* cd->is_privileged = 0;
* else
* cd->is_privileged = (__genwqe_readq(cd, IO_SLU_BITSTREAM)
* != IO_ILLEGAL_VALUE);
*/
static inline int genwqe_is_privileged(struct genwqe_dev *cd)
{
return cd->is_privileged;
}
#endif /* __CARD_BASE_H__ */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,188 @@
#ifndef __CARD_DDCB_H__
#define __CARD_DDCB_H__
/**
* IBM Accelerator Family 'GenWQE'
*
* (C) Copyright IBM Corp. 2013
*
* Author: Frank Haverkamp <haver@linux.vnet.ibm.com>
* Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com>
* Author: Michael Jung <mijung@de.ibm.com>
* Author: Michael Ruettger <michael@ibmra.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/types.h>
#include <asm/byteorder.h>
#include "genwqe_driver.h"
#include "card_base.h"
/**
* struct ddcb - Device Driver Control Block DDCB
* @hsi: Hardware software interlock
* @shi: Software hardware interlock. Hsi and shi are used to interlock
* software and hardware activities. We are using a compare and
* swap operation to ensure that there are no races when
* activating new DDCBs on the queue, or when we need to
* purge a DDCB from a running queue.
* @acfunc: Accelerator function addresses a unit within the chip
* @cmd: Command to work on
* @cmdopts_16: Options for the command
* @asiv: Input data
* @asv: Output data
*
* The DDCB data format is big endian. Multiple consequtive DDBCs form
* a DDCB queue.
*/
#define ASIV_LENGTH 104 /* Old specification without ATS field */
#define ASIV_LENGTH_ATS 96 /* New specification with ATS field */
#define ASV_LENGTH 64
struct ddcb {
union {
__be32 icrc_hsi_shi_32; /* iCRC, Hardware/SW interlock */
struct {
__be16 icrc_16;
u8 hsi;
u8 shi;
};
};
u8 pre; /* Preamble */
u8 xdir; /* Execution Directives */
__be16 seqnum_16; /* Sequence Number */
u8 acfunc; /* Accelerator Function.. */
u8 cmd; /* Command. */
__be16 cmdopts_16; /* Command Options */
u8 sur; /* Status Update Rate */
u8 psp; /* Protection Section Pointer */
__be16 rsvd_0e_16; /* Reserved invariant */
__be64 fwiv_64; /* Firmware Invariant. */
union {
struct {
__be64 ats_64; /* Address Translation Spec */
u8 asiv[ASIV_LENGTH_ATS]; /* New ASIV */
} n;
u8 __asiv[ASIV_LENGTH]; /* obsolete */
};
u8 asv[ASV_LENGTH]; /* Appl Spec Variant */
__be16 rsvd_c0_16; /* Reserved Variant */
__be16 vcrc_16; /* Variant CRC */
__be32 rsvd_32; /* Reserved unprotected */
__be64 deque_ts_64; /* Deque Time Stamp. */
__be16 retc_16; /* Return Code */
__be16 attn_16; /* Attention/Extended Error Codes */
__be32 progress_32; /* Progress indicator. */
__be64 cmplt_ts_64; /* Completion Time Stamp. */
/* The following layout matches the new service layer format */
__be32 ibdc_32; /* Inbound Data Count (* 256) */
__be32 obdc_32; /* Outbound Data Count (* 256) */
__be64 rsvd_SLH_64; /* Reserved for hardware */
union { /* private data for driver */
u8 priv[8];
__be64 priv_64;
};
__be64 disp_ts_64; /* Dispatch TimeStamp */
} __attribute__((__packed__));
/* CRC polynomials for DDCB */
#define CRC16_POLYNOMIAL 0x1021
/*
* SHI: Software to Hardware Interlock
* This 1 byte field is written by software to interlock the
* movement of one queue entry to another with the hardware in the
* chip.
*/
#define DDCB_SHI_INTR 0x04 /* Bit 2 */
#define DDCB_SHI_PURGE 0x02 /* Bit 1 */
#define DDCB_SHI_NEXT 0x01 /* Bit 0 */
/*
* HSI: Hardware to Software interlock
* This 1 byte field is written by hardware to interlock the movement
* of one queue entry to another with the software in the chip.
*/
#define DDCB_HSI_COMPLETED 0x40 /* Bit 6 */
#define DDCB_HSI_FETCHED 0x04 /* Bit 2 */
/*
* Accessing HSI/SHI is done 32-bit wide
* Normally 16-bit access would work too, but on some platforms the
* 16 compare and swap operation is not supported. Therefore
* switching to 32-bit such that those platforms will work too.
*
* iCRC HSI/SHI
*/
#define DDCB_INTR_BE32 cpu_to_be32(0x00000004)
#define DDCB_PURGE_BE32 cpu_to_be32(0x00000002)
#define DDCB_NEXT_BE32 cpu_to_be32(0x00000001)
#define DDCB_COMPLETED_BE32 cpu_to_be32(0x00004000)
#define DDCB_FETCHED_BE32 cpu_to_be32(0x00000400)
/* Definitions of DDCB presets */
#define DDCB_PRESET_PRE 0x80
#define ICRC_LENGTH(n) ((n) + 8 + 8 + 8) /* used ASIV + hdr fields */
#define VCRC_LENGTH(n) ((n)) /* used ASV */
/*
* Genwqe Scatter Gather list
* Each element has up to 8 entries.
* The chaining element is element 0 cause of prefetching needs.
*/
/*
* 0b0110 Chained descriptor. The descriptor is describing the next
* descriptor list.
*/
#define SG_CHAINED (0x6)
/*
* 0b0010 First entry of a descriptor list. Start from a Buffer-Empty
* condition.
*/
#define SG_DATA (0x2)
/*
* 0b0000 Early terminator. This is the last entry on the list
* irregardless of the length indicated.
*/
#define SG_END_LIST (0x0)
/**
* struct sglist - Scatter gather list
* @target_addr: Either a dma addr of memory to work on or a
* dma addr or a subsequent sglist block.
* @len: Length of the data block.
* @flags: See above.
*
* Depending on the command the GenWQE card can use a scatter gather
* list to describe the memory it works on. Always 8 sg_entry's form
* a block.
*/
struct sg_entry {
__be64 target_addr;
__be32 len;
__be32 flags;
};
#endif /* __CARD_DDCB_H__ */

View File

@ -0,0 +1,500 @@
/**
* IBM Accelerator Family 'GenWQE'
*
* (C) Copyright IBM Corp. 2013
*
* Author: Frank Haverkamp <haver@linux.vnet.ibm.com>
* Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com>
* Author: Michael Jung <mijung@de.ibm.com>
* Author: Michael Ruettger <michael@ibmra.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* Debugfs interfaces for the GenWQE card. Help to debug potential
* problems. Dump internal chip state for debugging and failure
* determination.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/debugfs.h>
#include <linux/seq_file.h>
#include <linux/uaccess.h>
#include "card_base.h"
#include "card_ddcb.h"
#define GENWQE_DEBUGFS_RO(_name, _showfn) \
static int genwqe_debugfs_##_name##_open(struct inode *inode, \
struct file *file) \
{ \
return single_open(file, _showfn, inode->i_private); \
} \
static const struct file_operations genwqe_##_name##_fops = { \
.open = genwqe_debugfs_##_name##_open, \
.read = seq_read, \
.llseek = seq_lseek, \
.release = single_release, \
}
static void dbg_uidn_show(struct seq_file *s, struct genwqe_reg *regs,
int entries)
{
unsigned int i;
u32 v_hi, v_lo;
for (i = 0; i < entries; i++) {
v_hi = (regs[i].val >> 32) & 0xffffffff;
v_lo = (regs[i].val) & 0xffffffff;
seq_printf(s, " 0x%08x 0x%08x 0x%08x 0x%08x EXT_ERR_REC\n",
regs[i].addr, regs[i].idx, v_hi, v_lo);
}
}
static int curr_dbg_uidn_show(struct seq_file *s, void *unused, int uid)
{
struct genwqe_dev *cd = s->private;
int entries;
struct genwqe_reg *regs;
entries = genwqe_ffdc_buff_size(cd, uid);
if (entries < 0)
return -EINVAL;
if (entries == 0)
return 0;
regs = kcalloc(entries, sizeof(*regs), GFP_KERNEL);
if (regs == NULL)
return -ENOMEM;
genwqe_stop_traps(cd); /* halt the traps while dumping data */
genwqe_ffdc_buff_read(cd, uid, regs, entries);
genwqe_start_traps(cd);
dbg_uidn_show(s, regs, entries);
kfree(regs);
return 0;
}
static int genwqe_curr_dbg_uid0_show(struct seq_file *s, void *unused)
{
return curr_dbg_uidn_show(s, unused, 0);
}
GENWQE_DEBUGFS_RO(curr_dbg_uid0, genwqe_curr_dbg_uid0_show);
static int genwqe_curr_dbg_uid1_show(struct seq_file *s, void *unused)
{
return curr_dbg_uidn_show(s, unused, 1);
}
GENWQE_DEBUGFS_RO(curr_dbg_uid1, genwqe_curr_dbg_uid1_show);
static int genwqe_curr_dbg_uid2_show(struct seq_file *s, void *unused)
{
return curr_dbg_uidn_show(s, unused, 2);
}
GENWQE_DEBUGFS_RO(curr_dbg_uid2, genwqe_curr_dbg_uid2_show);
static int prev_dbg_uidn_show(struct seq_file *s, void *unused, int uid)
{
struct genwqe_dev *cd = s->private;
dbg_uidn_show(s, cd->ffdc[uid].regs, cd->ffdc[uid].entries);
return 0;
}
static int genwqe_prev_dbg_uid0_show(struct seq_file *s, void *unused)
{
return prev_dbg_uidn_show(s, unused, 0);
}
GENWQE_DEBUGFS_RO(prev_dbg_uid0, genwqe_prev_dbg_uid0_show);
static int genwqe_prev_dbg_uid1_show(struct seq_file *s, void *unused)
{
return prev_dbg_uidn_show(s, unused, 1);
}
GENWQE_DEBUGFS_RO(prev_dbg_uid1, genwqe_prev_dbg_uid1_show);
static int genwqe_prev_dbg_uid2_show(struct seq_file *s, void *unused)
{
return prev_dbg_uidn_show(s, unused, 2);
}
GENWQE_DEBUGFS_RO(prev_dbg_uid2, genwqe_prev_dbg_uid2_show);
static int genwqe_curr_regs_show(struct seq_file *s, void *unused)
{
struct genwqe_dev *cd = s->private;
unsigned int i;
struct genwqe_reg *regs;
regs = kcalloc(GENWQE_FFDC_REGS, sizeof(*regs), GFP_KERNEL);
if (regs == NULL)
return -ENOMEM;
genwqe_stop_traps(cd);
genwqe_read_ffdc_regs(cd, regs, GENWQE_FFDC_REGS, 1);
genwqe_start_traps(cd);
for (i = 0; i < GENWQE_FFDC_REGS; i++) {
if (regs[i].addr == 0xffffffff)
break; /* invalid entries */
if (regs[i].val == 0x0ull)
continue; /* do not print 0x0 FIRs */
seq_printf(s, " 0x%08x 0x%016llx\n",
regs[i].addr, regs[i].val);
}
return 0;
}
GENWQE_DEBUGFS_RO(curr_regs, genwqe_curr_regs_show);
static int genwqe_prev_regs_show(struct seq_file *s, void *unused)
{
struct genwqe_dev *cd = s->private;
unsigned int i;
struct genwqe_reg *regs = cd->ffdc[GENWQE_DBG_REGS].regs;
if (regs == NULL)
return -EINVAL;
for (i = 0; i < GENWQE_FFDC_REGS; i++) {
if (regs[i].addr == 0xffffffff)
break; /* invalid entries */
if (regs[i].val == 0x0ull)
continue; /* do not print 0x0 FIRs */
seq_printf(s, " 0x%08x 0x%016llx\n",
regs[i].addr, regs[i].val);
}
return 0;
}
GENWQE_DEBUGFS_RO(prev_regs, genwqe_prev_regs_show);
static int genwqe_jtimer_show(struct seq_file *s, void *unused)
{
struct genwqe_dev *cd = s->private;
unsigned int vf_num;
u64 jtimer;
jtimer = genwqe_read_vreg(cd, IO_SLC_VF_APPJOB_TIMEOUT, 0);
seq_printf(s, " PF 0x%016llx %d msec\n", jtimer,
genwqe_pf_jobtimeout_msec);
for (vf_num = 0; vf_num < cd->num_vfs; vf_num++) {
jtimer = genwqe_read_vreg(cd, IO_SLC_VF_APPJOB_TIMEOUT,
vf_num + 1);
seq_printf(s, " VF%-2d 0x%016llx %d msec\n", vf_num, jtimer,
cd->vf_jobtimeout_msec[vf_num]);
}
return 0;
}
GENWQE_DEBUGFS_RO(jtimer, genwqe_jtimer_show);
static int genwqe_queue_working_time_show(struct seq_file *s, void *unused)
{
struct genwqe_dev *cd = s->private;
unsigned int vf_num;
u64 t;
t = genwqe_read_vreg(cd, IO_SLC_VF_QUEUE_WTIME, 0);
seq_printf(s, " PF 0x%016llx\n", t);
for (vf_num = 0; vf_num < cd->num_vfs; vf_num++) {
t = genwqe_read_vreg(cd, IO_SLC_VF_QUEUE_WTIME, vf_num + 1);
seq_printf(s, " VF%-2d 0x%016llx\n", vf_num, t);
}
return 0;
}
GENWQE_DEBUGFS_RO(queue_working_time, genwqe_queue_working_time_show);
static int genwqe_ddcb_info_show(struct seq_file *s, void *unused)
{
struct genwqe_dev *cd = s->private;
unsigned int i;
struct ddcb_queue *queue;
struct ddcb *pddcb;
queue = &cd->queue;
seq_puts(s, "DDCB QUEUE:\n");
seq_printf(s, " ddcb_max: %d\n"
" ddcb_daddr: %016llx - %016llx\n"
" ddcb_vaddr: %016llx\n"
" ddcbs_in_flight: %u\n"
" ddcbs_max_in_flight: %u\n"
" ddcbs_completed: %u\n"
" busy: %u\n"
" irqs_processed: %u\n",
queue->ddcb_max, (long long)queue->ddcb_daddr,
(long long)queue->ddcb_daddr +
(queue->ddcb_max * DDCB_LENGTH),
(long long)queue->ddcb_vaddr, queue->ddcbs_in_flight,
queue->ddcbs_max_in_flight, queue->ddcbs_completed,
queue->busy, cd->irqs_processed);
/* Hardware State */
seq_printf(s, " 0x%08x 0x%016llx IO_QUEUE_CONFIG\n"
" 0x%08x 0x%016llx IO_QUEUE_STATUS\n"
" 0x%08x 0x%016llx IO_QUEUE_SEGMENT\n"
" 0x%08x 0x%016llx IO_QUEUE_INITSQN\n"
" 0x%08x 0x%016llx IO_QUEUE_WRAP\n"
" 0x%08x 0x%016llx IO_QUEUE_OFFSET\n"
" 0x%08x 0x%016llx IO_QUEUE_WTIME\n"
" 0x%08x 0x%016llx IO_QUEUE_ERRCNTS\n"
" 0x%08x 0x%016llx IO_QUEUE_LRW\n",
queue->IO_QUEUE_CONFIG,
__genwqe_readq(cd, queue->IO_QUEUE_CONFIG),
queue->IO_QUEUE_STATUS,
__genwqe_readq(cd, queue->IO_QUEUE_STATUS),
queue->IO_QUEUE_SEGMENT,
__genwqe_readq(cd, queue->IO_QUEUE_SEGMENT),
queue->IO_QUEUE_INITSQN,
__genwqe_readq(cd, queue->IO_QUEUE_INITSQN),
queue->IO_QUEUE_WRAP,
__genwqe_readq(cd, queue->IO_QUEUE_WRAP),
queue->IO_QUEUE_OFFSET,
__genwqe_readq(cd, queue->IO_QUEUE_OFFSET),
queue->IO_QUEUE_WTIME,
__genwqe_readq(cd, queue->IO_QUEUE_WTIME),
queue->IO_QUEUE_ERRCNTS,
__genwqe_readq(cd, queue->IO_QUEUE_ERRCNTS),
queue->IO_QUEUE_LRW,
__genwqe_readq(cd, queue->IO_QUEUE_LRW));
seq_printf(s, "DDCB list (ddcb_act=%d/ddcb_next=%d):\n",
queue->ddcb_act, queue->ddcb_next);
pddcb = queue->ddcb_vaddr;
for (i = 0; i < queue->ddcb_max; i++) {
seq_printf(s, " %-3d: RETC=%03x SEQ=%04x HSI/SHI=%02x/%02x ",
i, be16_to_cpu(pddcb->retc_16),
be16_to_cpu(pddcb->seqnum_16),
pddcb->hsi, pddcb->shi);
seq_printf(s, "PRIV=%06llx CMD=%02x\n",
be64_to_cpu(pddcb->priv_64), pddcb->cmd);
pddcb++;
}
return 0;
}
GENWQE_DEBUGFS_RO(ddcb_info, genwqe_ddcb_info_show);
static int genwqe_info_show(struct seq_file *s, void *unused)
{
struct genwqe_dev *cd = s->private;
u16 val16, type;
u64 app_id, slu_id, bitstream = -1;
struct pci_dev *pci_dev = cd->pci_dev;
slu_id = __genwqe_readq(cd, IO_SLU_UNITCFG);
app_id = __genwqe_readq(cd, IO_APP_UNITCFG);
if (genwqe_is_privileged(cd))
bitstream = __genwqe_readq(cd, IO_SLU_BITSTREAM);
val16 = (u16)(slu_id & 0x0fLLU);
type = (u16)((slu_id >> 20) & 0xffLLU);
seq_printf(s, "%s driver version: %s\n"
" Device Name/Type: %s %s CardIdx: %d\n"
" SLU/APP Config : 0x%016llx/0x%016llx\n"
" Build Date : %u/%x/%u\n"
" Base Clock : %u MHz\n"
" Arch/SVN Release: %u/%llx\n"
" Bitstream : %llx\n",
GENWQE_DEVNAME, DRV_VERS_STRING, dev_name(&pci_dev->dev),
genwqe_is_privileged(cd) ?
"Physical" : "Virtual or no SR-IOV",
cd->card_idx, slu_id, app_id,
(u16)((slu_id >> 12) & 0x0fLLU), /* month */
(u16)((slu_id >> 4) & 0xffLLU), /* day */
(u16)((slu_id >> 16) & 0x0fLLU) + 2010, /* year */
genwqe_base_clock_frequency(cd),
(u16)((slu_id >> 32) & 0xffLLU), slu_id >> 40,
bitstream);
return 0;
}
GENWQE_DEBUGFS_RO(info, genwqe_info_show);
int genwqe_init_debugfs(struct genwqe_dev *cd)
{
struct dentry *root;
struct dentry *file;
int ret;
char card_name[64];
char name[64];
unsigned int i;
sprintf(card_name, "%s%u_card", GENWQE_DEVNAME, cd->card_idx);
root = debugfs_create_dir(card_name, cd->debugfs_genwqe);
if (!root) {
ret = -ENOMEM;
goto err0;
}
/* non privileged interfaces are done here */
file = debugfs_create_file("ddcb_info", S_IRUGO, root, cd,
&genwqe_ddcb_info_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_file("info", S_IRUGO, root, cd,
&genwqe_info_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_x64("err_inject", 0666, root, &cd->err_inject);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_u32("ddcb_software_timeout", 0666, root,
&cd->ddcb_software_timeout);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_u32("kill_timeout", 0666, root,
&cd->kill_timeout);
if (!file) {
ret = -ENOMEM;
goto err1;
}
/* privileged interfaces follow here */
if (!genwqe_is_privileged(cd)) {
cd->debugfs_root = root;
return 0;
}
file = debugfs_create_file("curr_regs", S_IRUGO, root, cd,
&genwqe_curr_regs_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_file("curr_dbg_uid0", S_IRUGO, root, cd,
&genwqe_curr_dbg_uid0_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_file("curr_dbg_uid1", S_IRUGO, root, cd,
&genwqe_curr_dbg_uid1_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_file("curr_dbg_uid2", S_IRUGO, root, cd,
&genwqe_curr_dbg_uid2_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_file("prev_regs", S_IRUGO, root, cd,
&genwqe_prev_regs_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_file("prev_dbg_uid0", S_IRUGO, root, cd,
&genwqe_prev_dbg_uid0_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_file("prev_dbg_uid1", S_IRUGO, root, cd,
&genwqe_prev_dbg_uid1_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_file("prev_dbg_uid2", S_IRUGO, root, cd,
&genwqe_prev_dbg_uid2_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
for (i = 0; i < GENWQE_MAX_VFS; i++) {
sprintf(name, "vf%d_jobtimeout_msec", i);
file = debugfs_create_u32(name, 0666, root,
&cd->vf_jobtimeout_msec[i]);
if (!file) {
ret = -ENOMEM;
goto err1;
}
}
file = debugfs_create_file("jobtimer", S_IRUGO, root, cd,
&genwqe_jtimer_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_file("queue_working_time", S_IRUGO, root, cd,
&genwqe_queue_working_time_fops);
if (!file) {
ret = -ENOMEM;
goto err1;
}
file = debugfs_create_u32("skip_recovery", 0666, root,
&cd->skip_recovery);
if (!file) {
ret = -ENOMEM;
goto err1;
}
cd->debugfs_root = root;
return 0;
err1:
debugfs_remove_recursive(root);
err0:
return ret;
}
void genqwe_exit_debugfs(struct genwqe_dev *cd)
{
debugfs_remove_recursive(cd->debugfs_root);
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,288 @@
/**
* IBM Accelerator Family 'GenWQE'
*
* (C) Copyright IBM Corp. 2013
*
* Author: Frank Haverkamp <haver@linux.vnet.ibm.com>
* Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com>
* Author: Michael Jung <mijung@de.ibm.com>
* Author: Michael Ruettger <michael@ibmra.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* Sysfs interfaces for the GenWQE card. There are attributes to query
* the version of the bitstream as well as some for the driver. For
* debugging, please also see the debugfs interfaces of this driver.
*/
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/types.h>
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/string.h>
#include <linux/fs.h>
#include <linux/sysfs.h>
#include <linux/ctype.h>
#include <linux/device.h>
#include "card_base.h"
#include "card_ddcb.h"
static const char * const genwqe_types[] = {
[GENWQE_TYPE_ALTERA_230] = "GenWQE4-230",
[GENWQE_TYPE_ALTERA_530] = "GenWQE4-530",
[GENWQE_TYPE_ALTERA_A4] = "GenWQE5-A4",
[GENWQE_TYPE_ALTERA_A7] = "GenWQE5-A7",
};
static ssize_t status_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
struct genwqe_dev *cd = dev_get_drvdata(dev);
const char *cs[GENWQE_CARD_STATE_MAX] = { "unused", "used", "error" };
return sprintf(buf, "%s\n", cs[cd->card_state]);
}
static DEVICE_ATTR_RO(status);
static ssize_t appid_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
char app_name[5];
struct genwqe_dev *cd = dev_get_drvdata(dev);
genwqe_read_app_id(cd, app_name, sizeof(app_name));
return sprintf(buf, "%s\n", app_name);
}
static DEVICE_ATTR_RO(appid);
static ssize_t version_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
u64 slu_id, app_id;
struct genwqe_dev *cd = dev_get_drvdata(dev);
slu_id = __genwqe_readq(cd, IO_SLU_UNITCFG);
app_id = __genwqe_readq(cd, IO_APP_UNITCFG);
return sprintf(buf, "%016llx.%016llx\n", slu_id, app_id);
}
static DEVICE_ATTR_RO(version);
static ssize_t type_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
u8 card_type;
struct genwqe_dev *cd = dev_get_drvdata(dev);
card_type = genwqe_card_type(cd);
return sprintf(buf, "%s\n", (card_type >= ARRAY_SIZE(genwqe_types)) ?
"invalid" : genwqe_types[card_type]);
}
static DEVICE_ATTR_RO(type);
static ssize_t driver_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
return sprintf(buf, "%s\n", DRV_VERS_STRING);
}
static DEVICE_ATTR_RO(driver);
static ssize_t tempsens_show(struct device *dev, struct device_attribute *attr,
char *buf)
{
u64 tempsens;
struct genwqe_dev *cd = dev_get_drvdata(dev);
tempsens = __genwqe_readq(cd, IO_SLU_TEMPERATURE_SENSOR);
return sprintf(buf, "%016llx\n", tempsens);
}
static DEVICE_ATTR_RO(tempsens);
static ssize_t freerunning_timer_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u64 t;
struct genwqe_dev *cd = dev_get_drvdata(dev);
t = __genwqe_readq(cd, IO_SLC_FREE_RUNNING_TIMER);
return sprintf(buf, "%016llx\n", t);
}
static DEVICE_ATTR_RO(freerunning_timer);
static ssize_t queue_working_time_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u64 t;
struct genwqe_dev *cd = dev_get_drvdata(dev);
t = __genwqe_readq(cd, IO_SLC_QUEUE_WTIME);
return sprintf(buf, "%016llx\n", t);
}
static DEVICE_ATTR_RO(queue_working_time);
static ssize_t base_clock_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
u64 base_clock;
struct genwqe_dev *cd = dev_get_drvdata(dev);
base_clock = genwqe_base_clock_frequency(cd);
return sprintf(buf, "%lld\n", base_clock);
}
static DEVICE_ATTR_RO(base_clock);
/**
* curr_bitstream_show() - Show the current bitstream id
*
* There is a bug in some old versions of the CPLD which selects the
* bitstream, which causes the IO_SLU_BITSTREAM register to report
* unreliable data in very rare cases. This makes this sysfs
* unreliable up to the point were a new CPLD version is being used.
*
* Unfortunately there is no automatic way yet to query the CPLD
* version, such that you need to manually ensure via programming
* tools that you have a recent version of the CPLD software.
*
* The proposed circumvention is to use a special recovery bitstream
* on the backup partition (0) to identify problems while loading the
* image.
*/
static ssize_t curr_bitstream_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int curr_bitstream;
struct genwqe_dev *cd = dev_get_drvdata(dev);
curr_bitstream = __genwqe_readq(cd, IO_SLU_BITSTREAM) & 0x1;
return sprintf(buf, "%d\n", curr_bitstream);
}
static DEVICE_ATTR_RO(curr_bitstream);
/**
* next_bitstream_show() - Show the next activated bitstream
*
* IO_SLC_CFGREG_SOFTRESET: This register can only be accessed by the PF.
*/
static ssize_t next_bitstream_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
int next_bitstream;
struct genwqe_dev *cd = dev_get_drvdata(dev);
switch ((cd->softreset & 0xc) >> 2) {
case 0x2:
next_bitstream = 0;
break;
case 0x3:
next_bitstream = 1;
break;
default:
next_bitstream = -1;
break; /* error */
}
return sprintf(buf, "%d\n", next_bitstream);
}
static ssize_t next_bitstream_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int partition;
struct genwqe_dev *cd = dev_get_drvdata(dev);
if (kstrtoint(buf, 0, &partition) < 0)
return -EINVAL;
switch (partition) {
case 0x0:
cd->softreset = 0x78;
break;
case 0x1:
cd->softreset = 0x7c;
break;
default:
return -EINVAL;
}
__genwqe_writeq(cd, IO_SLC_CFGREG_SOFTRESET, cd->softreset);
return count;
}
static DEVICE_ATTR_RW(next_bitstream);
/*
* Create device_attribute structures / params: name, mode, show, store
* additional flag if valid in VF
*/
static struct attribute *genwqe_attributes[] = {
&dev_attr_tempsens.attr,
&dev_attr_next_bitstream.attr,
&dev_attr_curr_bitstream.attr,
&dev_attr_base_clock.attr,
&dev_attr_driver.attr,
&dev_attr_type.attr,
&dev_attr_version.attr,
&dev_attr_appid.attr,
&dev_attr_status.attr,
&dev_attr_freerunning_timer.attr,
&dev_attr_queue_working_time.attr,
NULL,
};
static struct attribute *genwqe_normal_attributes[] = {
&dev_attr_driver.attr,
&dev_attr_type.attr,
&dev_attr_version.attr,
&dev_attr_appid.attr,
&dev_attr_status.attr,
&dev_attr_freerunning_timer.attr,
&dev_attr_queue_working_time.attr,
NULL,
};
/**
* genwqe_is_visible() - Determine if sysfs attribute should be visible or not
*
* VFs have restricted mmio capabilities, so not all sysfs entries
* are allowed in VFs.
*/
static umode_t genwqe_is_visible(struct kobject *kobj,
struct attribute *attr, int n)
{
unsigned int j;
struct device *dev = container_of(kobj, struct device, kobj);
struct genwqe_dev *cd = dev_get_drvdata(dev);
umode_t mode = attr->mode;
if (genwqe_is_privileged(cd))
return mode;
for (j = 0; genwqe_normal_attributes[j] != NULL; j++)
if (genwqe_normal_attributes[j] == attr)
return mode;
return 0;
}
static struct attribute_group genwqe_attribute_group = {
.is_visible = genwqe_is_visible,
.attrs = genwqe_attributes,
};
const struct attribute_group *genwqe_attribute_groups[] = {
&genwqe_attribute_group,
NULL,
};

View File

@ -0,0 +1,944 @@
/**
* IBM Accelerator Family 'GenWQE'
*
* (C) Copyright IBM Corp. 2013
*
* Author: Frank Haverkamp <haver@linux.vnet.ibm.com>
* Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com>
* Author: Michael Jung <mijung@de.ibm.com>
* Author: Michael Ruettger <michael@ibmra.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* Miscelanous functionality used in the other GenWQE driver parts.
*/
#include <linux/kernel.h>
#include <linux/dma-mapping.h>
#include <linux/sched.h>
#include <linux/vmalloc.h>
#include <linux/page-flags.h>
#include <linux/scatterlist.h>
#include <linux/hugetlb.h>
#include <linux/iommu.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/dma-mapping.h>
#include <linux/ctype.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <asm/pgtable.h>
#include "genwqe_driver.h"
#include "card_base.h"
#include "card_ddcb.h"
/**
* __genwqe_writeq() - Write 64-bit register
* @cd: genwqe device descriptor
* @byte_offs: byte offset within BAR
* @val: 64-bit value
*
* Return: 0 if success; < 0 if error
*/
int __genwqe_writeq(struct genwqe_dev *cd, u64 byte_offs, u64 val)
{
if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE)
return -EIO;
if (cd->mmio == NULL)
return -EIO;
__raw_writeq((__force u64)cpu_to_be64(val), cd->mmio + byte_offs);
return 0;
}
/**
* __genwqe_readq() - Read 64-bit register
* @cd: genwqe device descriptor
* @byte_offs: offset within BAR
*
* Return: value from register
*/
u64 __genwqe_readq(struct genwqe_dev *cd, u64 byte_offs)
{
if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE)
return 0xffffffffffffffffull;
if ((cd->err_inject & GENWQE_INJECT_GFIR_FATAL) &&
(byte_offs == IO_SLC_CFGREG_GFIR))
return 0x000000000000ffffull;
if ((cd->err_inject & GENWQE_INJECT_GFIR_INFO) &&
(byte_offs == IO_SLC_CFGREG_GFIR))
return 0x00000000ffff0000ull;
if (cd->mmio == NULL)
return 0xffffffffffffffffull;
return be64_to_cpu((__force __be64)__raw_readq(cd->mmio + byte_offs));
}
/**
* __genwqe_writel() - Write 32-bit register
* @cd: genwqe device descriptor
* @byte_offs: byte offset within BAR
* @val: 32-bit value
*
* Return: 0 if success; < 0 if error
*/
int __genwqe_writel(struct genwqe_dev *cd, u64 byte_offs, u32 val)
{
if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE)
return -EIO;
if (cd->mmio == NULL)
return -EIO;
__raw_writel((__force u32)cpu_to_be32(val), cd->mmio + byte_offs);
return 0;
}
/**
* __genwqe_readl() - Read 32-bit register
* @cd: genwqe device descriptor
* @byte_offs: offset within BAR
*
* Return: Value from register
*/
u32 __genwqe_readl(struct genwqe_dev *cd, u64 byte_offs)
{
if (cd->err_inject & GENWQE_INJECT_HARDWARE_FAILURE)
return 0xffffffff;
if (cd->mmio == NULL)
return 0xffffffff;
return be32_to_cpu((__force __be32)__raw_readl(cd->mmio + byte_offs));
}
/**
* genwqe_read_app_id() - Extract app_id
*
* app_unitcfg need to be filled with valid data first
*/
int genwqe_read_app_id(struct genwqe_dev *cd, char *app_name, int len)
{
int i, j;
u32 app_id = (u32)cd->app_unitcfg;
memset(app_name, 0, len);
for (i = 0, j = 0; j < min(len, 4); j++) {
char ch = (char)((app_id >> (24 - j*8)) & 0xff);
if (ch == ' ')
continue;
app_name[i++] = isprint(ch) ? ch : 'X';
}
return i;
}
/**
* genwqe_init_crc32() - Prepare a lookup table for fast crc32 calculations
*
* Existing kernel functions seem to use a different polynom,
* therefore we could not use them here.
*
* Genwqe's Polynomial = 0x20044009
*/
#define CRC32_POLYNOMIAL 0x20044009
static u32 crc32_tab[256]; /* crc32 lookup table */
void genwqe_init_crc32(void)
{
int i, j;
u32 crc;
for (i = 0; i < 256; i++) {
crc = i << 24;
for (j = 0; j < 8; j++) {
if (crc & 0x80000000)
crc = (crc << 1) ^ CRC32_POLYNOMIAL;
else
crc = (crc << 1);
}
crc32_tab[i] = crc;
}
}
/**
* genwqe_crc32() - Generate 32-bit crc as required for DDCBs
* @buff: pointer to data buffer
* @len: length of data for calculation
* @init: initial crc (0xffffffff at start)
*
* polynomial = x^32 * + x^29 + x^18 + x^14 + x^3 + 1 (0x20044009)
* Example: 4 bytes 0x01 0x02 0x03 0x04 with init=0xffffffff should
* result in a crc32 of 0xf33cb7d3.
*
* The existing kernel crc functions did not cover this polynom yet.
*
* Return: crc32 checksum.
*/
u32 genwqe_crc32(u8 *buff, size_t len, u32 init)
{
int i;
u32 crc;
crc = init;
while (len--) {
i = ((crc >> 24) ^ *buff++) & 0xFF;
crc = (crc << 8) ^ crc32_tab[i];
}
return crc;
}
void *__genwqe_alloc_consistent(struct genwqe_dev *cd, size_t size,
dma_addr_t *dma_handle)
{
if (get_order(size) > MAX_ORDER)
return NULL;
return pci_alloc_consistent(cd->pci_dev, size, dma_handle);
}
void __genwqe_free_consistent(struct genwqe_dev *cd, size_t size,
void *vaddr, dma_addr_t dma_handle)
{
if (vaddr == NULL)
return;
pci_free_consistent(cd->pci_dev, size, vaddr, dma_handle);
}
static void genwqe_unmap_pages(struct genwqe_dev *cd, dma_addr_t *dma_list,
int num_pages)
{
int i;
struct pci_dev *pci_dev = cd->pci_dev;
for (i = 0; (i < num_pages) && (dma_list[i] != 0x0); i++) {
pci_unmap_page(pci_dev, dma_list[i],
PAGE_SIZE, PCI_DMA_BIDIRECTIONAL);
dma_list[i] = 0x0;
}
}
static int genwqe_map_pages(struct genwqe_dev *cd,
struct page **page_list, int num_pages,
dma_addr_t *dma_list)
{
int i;
struct pci_dev *pci_dev = cd->pci_dev;
/* establish DMA mapping for requested pages */
for (i = 0; i < num_pages; i++) {
dma_addr_t daddr;
dma_list[i] = 0x0;
daddr = pci_map_page(pci_dev, page_list[i],
0, /* map_offs */
PAGE_SIZE,
PCI_DMA_BIDIRECTIONAL); /* FIXME rd/rw */
if (pci_dma_mapping_error(pci_dev, daddr)) {
dev_err(&pci_dev->dev,
"[%s] err: no dma addr daddr=%016llx!\n",
__func__, (long long)daddr);
goto err;
}
dma_list[i] = daddr;
}
return 0;
err:
genwqe_unmap_pages(cd, dma_list, num_pages);
return -EIO;
}
static int genwqe_sgl_size(int num_pages)
{
int len, num_tlb = num_pages / 7;
len = sizeof(struct sg_entry) * (num_pages+num_tlb + 1);
return roundup(len, PAGE_SIZE);
}
struct sg_entry *genwqe_alloc_sgl(struct genwqe_dev *cd, int num_pages,
dma_addr_t *dma_addr, size_t *sgl_size)
{
struct pci_dev *pci_dev = cd->pci_dev;
struct sg_entry *sgl;
*sgl_size = genwqe_sgl_size(num_pages);
if (get_order(*sgl_size) > MAX_ORDER) {
dev_err(&pci_dev->dev,
"[%s] err: too much memory requested!\n", __func__);
return NULL;
}
sgl = __genwqe_alloc_consistent(cd, *sgl_size, dma_addr);
if (sgl == NULL) {
dev_err(&pci_dev->dev,
"[%s] err: no memory available!\n", __func__);
return NULL;
}
return sgl;
}
int genwqe_setup_sgl(struct genwqe_dev *cd,
unsigned long offs,
unsigned long size,
struct sg_entry *sgl,
dma_addr_t dma_addr, size_t sgl_size,
dma_addr_t *dma_list, int page_offs, int num_pages)
{
int i = 0, j = 0, p;
unsigned long dma_offs, map_offs;
struct pci_dev *pci_dev = cd->pci_dev;
dma_addr_t prev_daddr = 0;
struct sg_entry *s, *last_s = NULL;
/* sanity checks */
if (offs > PAGE_SIZE) {
dev_err(&pci_dev->dev,
"[%s] too large start offs %08lx\n", __func__, offs);
return -EFAULT;
}
if (sgl_size < genwqe_sgl_size(num_pages)) {
dev_err(&pci_dev->dev,
"[%s] sgl_size too small %08lx for %d pages\n",
__func__, sgl_size, num_pages);
return -EFAULT;
}
dma_offs = 128; /* next block if needed/dma_offset */
map_offs = offs; /* offset in first page */
s = &sgl[0]; /* first set of 8 entries */
p = 0; /* page */
while (p < num_pages) {
dma_addr_t daddr;
unsigned int size_to_map;
/* always write the chaining entry, cleanup is done later */
j = 0;
s[j].target_addr = cpu_to_be64(dma_addr + dma_offs);
s[j].len = cpu_to_be32(128);
s[j].flags = cpu_to_be32(SG_CHAINED);
j++;
while (j < 8) {
/* DMA mapping for requested page, offs, size */
size_to_map = min(size, PAGE_SIZE - map_offs);
daddr = dma_list[page_offs + p] + map_offs;
size -= size_to_map;
map_offs = 0;
if (prev_daddr == daddr) {
u32 prev_len = be32_to_cpu(last_s->len);
/* pr_info("daddr combining: "
"%016llx/%08x -> %016llx\n",
prev_daddr, prev_len, daddr); */
last_s->len = cpu_to_be32(prev_len +
size_to_map);
p++; /* process next page */
if (p == num_pages)
goto fixup; /* nothing to do */
prev_daddr = daddr + size_to_map;
continue;
}
/* start new entry */
s[j].target_addr = cpu_to_be64(daddr);
s[j].len = cpu_to_be32(size_to_map);
s[j].flags = cpu_to_be32(SG_DATA);
prev_daddr = daddr + size_to_map;
last_s = &s[j];
j++;
p++; /* process next page */
if (p == num_pages)
goto fixup; /* nothing to do */
}
dma_offs += 128;
s += 8; /* continue 8 elements further */
}
fixup:
if (j == 1) { /* combining happend on last entry! */
s -= 8; /* full shift needed on previous sgl block */
j = 7; /* shift all elements */
}
for (i = 0; i < j; i++) /* move elements 1 up */
s[i] = s[i + 1];
s[i].target_addr = cpu_to_be64(0);
s[i].len = cpu_to_be32(0);
s[i].flags = cpu_to_be32(SG_END_LIST);
return 0;
}
void genwqe_free_sgl(struct genwqe_dev *cd, struct sg_entry *sg_list,
dma_addr_t dma_addr, size_t size)
{
__genwqe_free_consistent(cd, size, sg_list, dma_addr);
}
/**
* free_user_pages() - Give pinned pages back
*
* Documentation of get_user_pages is in mm/memory.c:
*
* If the page is written to, set_page_dirty (or set_page_dirty_lock,
* as appropriate) must be called after the page is finished with, and
* before put_page is called.
*
* FIXME Could be of use to others and might belong in the generic
* code, if others agree. E.g.
* ll_free_user_pages in drivers/staging/lustre/lustre/llite/rw26.c
* ceph_put_page_vector in net/ceph/pagevec.c
* maybe more?
*/
static int free_user_pages(struct page **page_list, unsigned int nr_pages,
int dirty)
{
unsigned int i;
for (i = 0; i < nr_pages; i++) {
if (page_list[i] != NULL) {
if (dirty)
set_page_dirty_lock(page_list[i]);
put_page(page_list[i]);
}
}
return 0;
}
/**
* genwqe_user_vmap() - Map user-space memory to virtual kernel memory
* @cd: pointer to genwqe device
* @m: mapping params
* @uaddr: user virtual address
* @size: size of memory to be mapped
*
* We need to think about how we could speed this up. Of course it is
* not a good idea to do this over and over again, like we are
* currently doing it. Nevertheless, I am curious where on the path
* the performance is spend. Most probably within the memory
* allocation functions, but maybe also in the DMA mapping code.
*
* Restrictions: The maximum size of the possible mapping currently depends
* on the amount of memory we can get using kzalloc() for the
* page_list and pci_alloc_consistent for the sg_list.
* The sg_list is currently itself not scattered, which could
* be fixed with some effort. The page_list must be split into
* PAGE_SIZE chunks too. All that will make the complicated
* code more complicated.
*
* Return: 0 if success
*/
int genwqe_user_vmap(struct genwqe_dev *cd, struct dma_mapping *m, void *uaddr,
unsigned long size, struct ddcb_requ *req)
{
int rc = -EINVAL;
unsigned long data, offs;
struct pci_dev *pci_dev = cd->pci_dev;
if ((uaddr == NULL) || (size == 0)) {
m->size = 0; /* mark unused and not added */
return -EINVAL;
}
m->u_vaddr = uaddr;
m->size = size;
/* determine space needed for page_list. */
data = (unsigned long)uaddr;
offs = offset_in_page(data);
m->nr_pages = DIV_ROUND_UP(offs + size, PAGE_SIZE);
m->page_list = kcalloc(m->nr_pages,
sizeof(struct page *) + sizeof(dma_addr_t),
GFP_KERNEL);
if (!m->page_list) {
dev_err(&pci_dev->dev, "err: alloc page_list failed\n");
m->nr_pages = 0;
m->u_vaddr = NULL;
m->size = 0; /* mark unused and not added */
return -ENOMEM;
}
m->dma_list = (dma_addr_t *)(m->page_list + m->nr_pages);
/* pin user pages in memory */
rc = get_user_pages_fast(data & PAGE_MASK, /* page aligned addr */
m->nr_pages,
1, /* write by caller */
m->page_list); /* ptrs to pages */
/* assumption: get_user_pages can be killed by signals. */
if (rc < m->nr_pages) {
free_user_pages(m->page_list, rc, 0);
rc = -EFAULT;
goto fail_get_user_pages;
}
rc = genwqe_map_pages(cd, m->page_list, m->nr_pages, m->dma_list);
if (rc != 0)
goto fail_free_user_pages;
return 0;
fail_free_user_pages:
free_user_pages(m->page_list, m->nr_pages, 0);
fail_get_user_pages:
kfree(m->page_list);
m->page_list = NULL;
m->dma_list = NULL;
m->nr_pages = 0;
m->u_vaddr = NULL;
m->size = 0; /* mark unused and not added */
return rc;
}
/**
* genwqe_user_vunmap() - Undo mapping of user-space mem to virtual kernel
* memory
* @cd: pointer to genwqe device
* @m: mapping params
*/
int genwqe_user_vunmap(struct genwqe_dev *cd, struct dma_mapping *m,
struct ddcb_requ *req)
{
struct pci_dev *pci_dev = cd->pci_dev;
if (!dma_mapping_used(m)) {
dev_err(&pci_dev->dev, "[%s] err: mapping %p not used!\n",
__func__, m);
return -EINVAL;
}
if (m->dma_list)
genwqe_unmap_pages(cd, m->dma_list, m->nr_pages);
if (m->page_list) {
free_user_pages(m->page_list, m->nr_pages, 1);
kfree(m->page_list);
m->page_list = NULL;
m->dma_list = NULL;
m->nr_pages = 0;
}
m->u_vaddr = NULL;
m->size = 0; /* mark as unused and not added */
return 0;
}
/**
* genwqe_card_type() - Get chip type SLU Configuration Register
* @cd: pointer to the genwqe device descriptor
* Return: 0: Altera Stratix-IV 230
* 1: Altera Stratix-IV 530
* 2: Altera Stratix-V A4
* 3: Altera Stratix-V A7
*/
u8 genwqe_card_type(struct genwqe_dev *cd)
{
u64 card_type = cd->slu_unitcfg;
return (u8)((card_type & IO_SLU_UNITCFG_TYPE_MASK) >> 20);
}
/**
* genwqe_card_reset() - Reset the card
* @cd: pointer to the genwqe device descriptor
*/
int genwqe_card_reset(struct genwqe_dev *cd)
{
u64 softrst;
struct pci_dev *pci_dev = cd->pci_dev;
if (!genwqe_is_privileged(cd))
return -ENODEV;
/* new SL */
__genwqe_writeq(cd, IO_SLC_CFGREG_SOFTRESET, 0x1ull);
msleep(1000);
__genwqe_readq(cd, IO_HSU_FIR_CLR);
__genwqe_readq(cd, IO_APP_FIR_CLR);
__genwqe_readq(cd, IO_SLU_FIR_CLR);
/*
* Read-modify-write to preserve the stealth bits
*
* For SL >= 039, Stealth WE bit allows removing
* the read-modify-wrote.
* r-m-w may require a mask 0x3C to avoid hitting hard
* reset again for error reset (should be 0, chicken).
*/
softrst = __genwqe_readq(cd, IO_SLC_CFGREG_SOFTRESET) & 0x3cull;
__genwqe_writeq(cd, IO_SLC_CFGREG_SOFTRESET, softrst | 0x2ull);
/* give ERRORRESET some time to finish */
msleep(50);
if (genwqe_need_err_masking(cd)) {
dev_info(&pci_dev->dev,
"[%s] masking errors for old bitstreams\n", __func__);
__genwqe_writeq(cd, IO_SLC_MISC_DEBUG, 0x0aull);
}
return 0;
}
int genwqe_read_softreset(struct genwqe_dev *cd)
{
u64 bitstream;
if (!genwqe_is_privileged(cd))
return -ENODEV;
bitstream = __genwqe_readq(cd, IO_SLU_BITSTREAM) & 0x1;
cd->softreset = (bitstream == 0) ? 0x8ull : 0xcull;
return 0;
}
/**
* genwqe_set_interrupt_capability() - Configure MSI capability structure
* @cd: pointer to the device
* Return: 0 if no error
*/
int genwqe_set_interrupt_capability(struct genwqe_dev *cd, int count)
{
int rc;
struct pci_dev *pci_dev = cd->pci_dev;
rc = pci_enable_msi_block(pci_dev, count);
if (rc == 0)
cd->flags |= GENWQE_FLAG_MSI_ENABLED;
return rc;
}
/**
* genwqe_reset_interrupt_capability() - Undo genwqe_set_interrupt_capability()
* @cd: pointer to the device
*/
void genwqe_reset_interrupt_capability(struct genwqe_dev *cd)
{
struct pci_dev *pci_dev = cd->pci_dev;
if (cd->flags & GENWQE_FLAG_MSI_ENABLED) {
pci_disable_msi(pci_dev);
cd->flags &= ~GENWQE_FLAG_MSI_ENABLED;
}
}
/**
* set_reg_idx() - Fill array with data. Ignore illegal offsets.
* @cd: card device
* @r: debug register array
* @i: index to desired entry
* @m: maximum possible entries
* @addr: addr which is read
* @index: index in debug array
* @val: read value
*/
static int set_reg_idx(struct genwqe_dev *cd, struct genwqe_reg *r,
unsigned int *i, unsigned int m, u32 addr, u32 idx,
u64 val)
{
if (WARN_ON_ONCE(*i >= m))
return -EFAULT;
r[*i].addr = addr;
r[*i].idx = idx;
r[*i].val = val;
++*i;
return 0;
}
static int set_reg(struct genwqe_dev *cd, struct genwqe_reg *r,
unsigned int *i, unsigned int m, u32 addr, u64 val)
{
return set_reg_idx(cd, r, i, m, addr, 0, val);
}
int genwqe_read_ffdc_regs(struct genwqe_dev *cd, struct genwqe_reg *regs,
unsigned int max_regs, int all)
{
unsigned int i, j, idx = 0;
u32 ufir_addr, ufec_addr, sfir_addr, sfec_addr;
u64 gfir, sluid, appid, ufir, ufec, sfir, sfec;
/* Global FIR */
gfir = __genwqe_readq(cd, IO_SLC_CFGREG_GFIR);
set_reg(cd, regs, &idx, max_regs, IO_SLC_CFGREG_GFIR, gfir);
/* UnitCfg for SLU */
sluid = __genwqe_readq(cd, IO_SLU_UNITCFG); /* 0x00000000 */
set_reg(cd, regs, &idx, max_regs, IO_SLU_UNITCFG, sluid);
/* UnitCfg for APP */
appid = __genwqe_readq(cd, IO_APP_UNITCFG); /* 0x02000000 */
set_reg(cd, regs, &idx, max_regs, IO_APP_UNITCFG, appid);
/* Check all chip Units */
for (i = 0; i < GENWQE_MAX_UNITS; i++) {
/* Unit FIR */
ufir_addr = (i << 24) | 0x008;
ufir = __genwqe_readq(cd, ufir_addr);
set_reg(cd, regs, &idx, max_regs, ufir_addr, ufir);
/* Unit FEC */
ufec_addr = (i << 24) | 0x018;
ufec = __genwqe_readq(cd, ufec_addr);
set_reg(cd, regs, &idx, max_regs, ufec_addr, ufec);
for (j = 0; j < 64; j++) {
/* wherever there is a primary 1, read the 2ndary */
if (!all && (!(ufir & (1ull << j))))
continue;
sfir_addr = (i << 24) | (0x100 + 8 * j);
sfir = __genwqe_readq(cd, sfir_addr);
set_reg(cd, regs, &idx, max_regs, sfir_addr, sfir);
sfec_addr = (i << 24) | (0x300 + 8 * j);
sfec = __genwqe_readq(cd, sfec_addr);
set_reg(cd, regs, &idx, max_regs, sfec_addr, sfec);
}
}
/* fill with invalid data until end */
for (i = idx; i < max_regs; i++) {
regs[i].addr = 0xffffffff;
regs[i].val = 0xffffffffffffffffull;
}
return idx;
}
/**
* genwqe_ffdc_buff_size() - Calculates the number of dump registers
*/
int genwqe_ffdc_buff_size(struct genwqe_dev *cd, int uid)
{
int entries = 0, ring, traps, traces, trace_entries;
u32 eevptr_addr, l_addr, d_len, d_type;
u64 eevptr, val, addr;
eevptr_addr = GENWQE_UID_OFFS(uid) | IO_EXTENDED_ERROR_POINTER;
eevptr = __genwqe_readq(cd, eevptr_addr);
if ((eevptr != 0x0) && (eevptr != -1ull)) {
l_addr = GENWQE_UID_OFFS(uid) | eevptr;
while (1) {
val = __genwqe_readq(cd, l_addr);
if ((val == 0x0) || (val == -1ull))
break;
/* 38:24 */
d_len = (val & 0x0000007fff000000ull) >> 24;
/* 39 */
d_type = (val & 0x0000008000000000ull) >> 36;
if (d_type) { /* repeat */
entries += d_len;
} else { /* size in bytes! */
entries += d_len >> 3;
}
l_addr += 8;
}
}
for (ring = 0; ring < 8; ring++) {
addr = GENWQE_UID_OFFS(uid) | IO_EXTENDED_DIAG_MAP(ring);
val = __genwqe_readq(cd, addr);
if ((val == 0x0ull) || (val == -1ull))
continue;
traps = (val >> 24) & 0xff;
traces = (val >> 16) & 0xff;
trace_entries = val & 0xffff;
entries += traps + (traces * trace_entries);
}
return entries;
}
/**
* genwqe_ffdc_buff_read() - Implements LogoutExtendedErrorRegisters procedure
*/
int genwqe_ffdc_buff_read(struct genwqe_dev *cd, int uid,
struct genwqe_reg *regs, unsigned int max_regs)
{
int i, traps, traces, trace, trace_entries, trace_entry, ring;
unsigned int idx = 0;
u32 eevptr_addr, l_addr, d_addr, d_len, d_type;
u64 eevptr, e, val, addr;
eevptr_addr = GENWQE_UID_OFFS(uid) | IO_EXTENDED_ERROR_POINTER;
eevptr = __genwqe_readq(cd, eevptr_addr);
if ((eevptr != 0x0) && (eevptr != 0xffffffffffffffffull)) {
l_addr = GENWQE_UID_OFFS(uid) | eevptr;
while (1) {
e = __genwqe_readq(cd, l_addr);
if ((e == 0x0) || (e == 0xffffffffffffffffull))
break;
d_addr = (e & 0x0000000000ffffffull); /* 23:0 */
d_len = (e & 0x0000007fff000000ull) >> 24; /* 38:24 */
d_type = (e & 0x0000008000000000ull) >> 36; /* 39 */
d_addr |= GENWQE_UID_OFFS(uid);
if (d_type) {
for (i = 0; i < (int)d_len; i++) {
val = __genwqe_readq(cd, d_addr);
set_reg_idx(cd, regs, &idx, max_regs,
d_addr, i, val);
}
} else {
d_len >>= 3; /* Size in bytes! */
for (i = 0; i < (int)d_len; i++, d_addr += 8) {
val = __genwqe_readq(cd, d_addr);
set_reg_idx(cd, regs, &idx, max_regs,
d_addr, 0, val);
}
}
l_addr += 8;
}
}
/*
* To save time, there are only 6 traces poplulated on Uid=2,
* Ring=1. each with iters=512.
*/
for (ring = 0; ring < 8; ring++) { /* 0 is fls, 1 is fds,
2...7 are ASI rings */
addr = GENWQE_UID_OFFS(uid) | IO_EXTENDED_DIAG_MAP(ring);
val = __genwqe_readq(cd, addr);
if ((val == 0x0ull) || (val == -1ull))
continue;
traps = (val >> 24) & 0xff; /* Number of Traps */
traces = (val >> 16) & 0xff; /* Number of Traces */
trace_entries = val & 0xffff; /* Entries per trace */
/* Note: This is a combined loop that dumps both the traps */
/* (for the trace == 0 case) as well as the traces 1 to */
/* 'traces'. */
for (trace = 0; trace <= traces; trace++) {
u32 diag_sel =
GENWQE_EXTENDED_DIAG_SELECTOR(ring, trace);
addr = (GENWQE_UID_OFFS(uid) |
IO_EXTENDED_DIAG_SELECTOR);
__genwqe_writeq(cd, addr, diag_sel);
for (trace_entry = 0;
trace_entry < (trace ? trace_entries : traps);
trace_entry++) {
addr = (GENWQE_UID_OFFS(uid) |
IO_EXTENDED_DIAG_READ_MBX);
val = __genwqe_readq(cd, addr);
set_reg_idx(cd, regs, &idx, max_regs, addr,
(diag_sel<<16) | trace_entry, val);
}
}
}
return 0;
}
/**
* genwqe_write_vreg() - Write register in virtual window
*
* Note, these registers are only accessible to the PF through the
* VF-window. It is not intended for the VF to access.
*/
int genwqe_write_vreg(struct genwqe_dev *cd, u32 reg, u64 val, int func)
{
__genwqe_writeq(cd, IO_PF_SLC_VIRTUAL_WINDOW, func & 0xf);
__genwqe_writeq(cd, reg, val);
return 0;
}
/**
* genwqe_read_vreg() - Read register in virtual window
*
* Note, these registers are only accessible to the PF through the
* VF-window. It is not intended for the VF to access.
*/
u64 genwqe_read_vreg(struct genwqe_dev *cd, u32 reg, int func)
{
__genwqe_writeq(cd, IO_PF_SLC_VIRTUAL_WINDOW, func & 0xf);
return __genwqe_readq(cd, reg);
}
/**
* genwqe_base_clock_frequency() - Deteremine base clock frequency of the card
*
* Note: From a design perspective it turned out to be a bad idea to
* use codes here to specifiy the frequency/speed values. An old
* driver cannot understand new codes and is therefore always a
* problem. Better is to measure out the value or put the
* speed/frequency directly into a register which is always a valid
* value for old as well as for new software.
*
* Return: Card clock in MHz
*/
int genwqe_base_clock_frequency(struct genwqe_dev *cd)
{
u16 speed; /* MHz MHz MHz MHz */
static const int speed_grade[] = { 250, 200, 166, 175 };
speed = (u16)((cd->slu_unitcfg >> 28) & 0x0full);
if (speed >= ARRAY_SIZE(speed_grade))
return 0; /* illegal value */
return speed_grade[speed];
}
/**
* genwqe_stop_traps() - Stop traps
*
* Before reading out the analysis data, we need to stop the traps.
*/
void genwqe_stop_traps(struct genwqe_dev *cd)
{
__genwqe_writeq(cd, IO_SLC_MISC_DEBUG_SET, 0xcull);
}
/**
* genwqe_start_traps() - Start traps
*
* After having read the data, we can/must enable the traps again.
*/
void genwqe_start_traps(struct genwqe_dev *cd)
{
__genwqe_writeq(cd, IO_SLC_MISC_DEBUG_CLR, 0xcull);
if (genwqe_need_err_masking(cd))
__genwqe_writeq(cd, IO_SLC_MISC_DEBUG, 0x0aull);
}

View File

@ -0,0 +1,77 @@
#ifndef __GENWQE_DRIVER_H__
#define __GENWQE_DRIVER_H__
/**
* IBM Accelerator Family 'GenWQE'
*
* (C) Copyright IBM Corp. 2013
*
* Author: Frank Haverkamp <haver@linux.vnet.ibm.com>
* Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com>
* Author: Michael Jung <mijung@de.ibm.com>
* Author: Michael Ruettger <michael@ibmra.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include <linux/types.h>
#include <linux/stddef.h>
#include <linux/cdev.h>
#include <linux/list.h>
#include <linux/kthread.h>
#include <linux/scatterlist.h>
#include <linux/iommu.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/printk.h>
#include <asm/byteorder.h>
#include <linux/genwqe/genwqe_card.h>
#define DRV_VERS_STRING "2.0.0"
/*
* Static minor number assignement, until we decide/implement
* something dynamic.
*/
#define GENWQE_MAX_MINOR 128 /* up to 128 possible genwqe devices */
/**
* genwqe_requ_alloc() - Allocate a new DDCB execution request
*
* This data structure contains the user visiable fields of the DDCB
* to be executed.
*
* Return: ptr to genwqe_ddcb_cmd data structure
*/
struct genwqe_ddcb_cmd *ddcb_requ_alloc(void);
/**
* ddcb_requ_free() - Free DDCB execution request.
* @req: ptr to genwqe_ddcb_cmd data structure.
*/
void ddcb_requ_free(struct genwqe_ddcb_cmd *req);
u32 genwqe_crc32(u8 *buff, size_t len, u32 init);
static inline void genwqe_hexdump(struct pci_dev *pci_dev,
const void *buff, unsigned int size)
{
char prefix[32];
scnprintf(prefix, sizeof(prefix), "%s %s: ",
GENWQE_DEVNAME, pci_name(pci_dev));
print_hex_dump_debug(prefix, DUMP_PREFIX_OFFSET, 16, 1, buff,
size, true);
}
#endif /* __GENWQE_DRIVER_H__ */

View File

@ -224,7 +224,7 @@ static int jp_scsi_dispatch_cmd(struct scsi_cmnd *cmd)
}
#ifdef CONFIG_IDE
int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file,
static int jp_generic_ide_ioctl(ide_drive_t *drive, struct file *file,
struct block_device *bdev, unsigned int cmd,
unsigned long arg)
{
@ -334,9 +334,10 @@ static void execute_location(void *dst)
static void execute_user_location(void *dst)
{
/* Intentionally crossing kernel/user memory boundary. */
void (*func)(void) = dst;
if (copy_to_user(dst, do_nothing, EXEC_SIZE))
if (copy_to_user((void __user *)dst, do_nothing, EXEC_SIZE))
return;
func();
}
@ -408,6 +409,8 @@ static void lkdtm_do_action(enum ctype which)
case CT_SPINLOCKUP:
/* Must be called twice to trigger. */
spin_lock(&lock_me_up);
/* Let sparse know we intended to exit holding the lock. */
__release(&lock_me_up);
break;
case CT_HUNG_TASK:
set_current_state(TASK_UNINTERRUPTIBLE);

View File

@ -177,7 +177,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
unsigned long timeout;
int i;
/* Only Posible if we are in timeout */
/* Only possible if we are in timeout */
if (!cl || cl != &dev->iamthif_cl) {
dev_dbg(&dev->pdev->dev, "bad file ext.\n");
return -ETIMEDOUT;
@ -249,7 +249,7 @@ int mei_amthif_read(struct mei_device *dev, struct file *file,
cb->response_buffer.size);
dev_dbg(&dev->pdev->dev, "amthif cb->buf_idx - %lu\n", cb->buf_idx);
/* length is being turncated to PAGE_SIZE, however,
/* length is being truncated to PAGE_SIZE, however,
* the buf_idx may point beyond */
length = min_t(size_t, length, (cb->buf_idx - *offset));
@ -316,6 +316,7 @@ static int mei_amthif_send_cmd(struct mei_device *dev, struct mei_cl_cb *cb)
mei_hdr.host_addr = dev->iamthif_cl.host_client_id;
mei_hdr.me_addr = dev->iamthif_cl.me_client_id;
mei_hdr.reserved = 0;
mei_hdr.internal = 0;
dev->iamthif_msg_buf_index += mei_hdr.length;
ret = mei_write_message(dev, &mei_hdr, dev->iamthif_msg_buf);
if (ret)
@ -477,6 +478,7 @@ int mei_amthif_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
mei_hdr.host_addr = cl->host_client_id;
mei_hdr.me_addr = cl->me_client_id;
mei_hdr.reserved = 0;
mei_hdr.internal = 0;
if (*slots >= msg_slots) {
mei_hdr.length = len;

View File

@ -154,7 +154,7 @@ int mei_io_cb_alloc_req_buf(struct mei_cl_cb *cb, size_t length)
return 0;
}
/**
* mei_io_cb_alloc_resp_buf - allocate respose buffer
* mei_io_cb_alloc_resp_buf - allocate response buffer
*
* @cb: io callback structure
* @length: size of the buffer
@ -207,7 +207,7 @@ int mei_cl_flush_queues(struct mei_cl *cl)
/**
* mei_cl_init - initializes intialize cl.
* mei_cl_init - initializes cl.
*
* @cl: host client to be initialized
* @dev: mei device
@ -263,10 +263,10 @@ struct mei_cl_cb *mei_cl_find_read_cb(struct mei_cl *cl)
return NULL;
}
/** mei_cl_link: allocte host id in the host map
/** mei_cl_link: allocate host id in the host map
*
* @cl - host client
* @id - fixed host id or -1 for genereting one
* @id - fixed host id or -1 for generic one
*
* returns 0 on success
* -EINVAL on incorrect values
@ -282,19 +282,19 @@ int mei_cl_link(struct mei_cl *cl, int id)
dev = cl->dev;
/* If Id is not asigned get one*/
/* If Id is not assigned get one*/
if (id == MEI_HOST_CLIENT_ID_ANY)
id = find_first_zero_bit(dev->host_clients_map,
MEI_CLIENTS_MAX);
if (id >= MEI_CLIENTS_MAX) {
dev_err(&dev->pdev->dev, "id exceded %d", MEI_CLIENTS_MAX) ;
dev_err(&dev->pdev->dev, "id exceeded %d", MEI_CLIENTS_MAX);
return -EMFILE;
}
open_handle_count = dev->open_handle_count + dev->iamthif_open_count;
if (open_handle_count >= MEI_MAX_OPEN_HANDLE_COUNT) {
dev_err(&dev->pdev->dev, "open_handle_count exceded %d",
dev_err(&dev->pdev->dev, "open_handle_count exceeded %d",
MEI_MAX_OPEN_HANDLE_COUNT);
return -EMFILE;
}
@ -344,8 +344,6 @@ int mei_cl_unlink(struct mei_cl *cl)
cl->state = MEI_FILE_INITIALIZING;
list_del_init(&cl->link);
return 0;
}
@ -372,13 +370,14 @@ void mei_host_client_init(struct work_struct *work)
}
dev->dev_state = MEI_DEV_ENABLED;
dev->reset_count = 0;
mutex_unlock(&dev->device_lock);
}
/**
* mei_cl_disconnect - disconnect host clinet form the me one
* mei_cl_disconnect - disconnect host client from the me one
*
* @cl: host client
*
@ -457,7 +456,7 @@ free:
*
* @cl: private data of the file object
*
* returns ture if other client is connected, 0 - otherwise.
* returns true if other client is connected, false - otherwise.
*/
bool mei_cl_is_other_connecting(struct mei_cl *cl)
{
@ -481,7 +480,7 @@ bool mei_cl_is_other_connecting(struct mei_cl *cl)
}
/**
* mei_cl_connect - connect host clinet to the me one
* mei_cl_connect - connect host client to the me one
*
* @cl: host client
*
@ -729,6 +728,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
mei_hdr.host_addr = cl->host_client_id;
mei_hdr.me_addr = cl->me_client_id;
mei_hdr.reserved = 0;
mei_hdr.internal = cb->internal;
if (*slots >= msg_slots) {
mei_hdr.length = len;
@ -775,7 +775,7 @@ int mei_cl_irq_write_complete(struct mei_cl *cl, struct mei_cl_cb *cb,
* @cl: host client
* @cl: write callback with filled data
*
* returns numbe of bytes sent on success, <0 on failure.
* returns number of bytes sent on success, <0 on failure.
*/
int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
{
@ -828,6 +828,7 @@ int mei_cl_write(struct mei_cl *cl, struct mei_cl_cb *cb, bool blocking)
mei_hdr.host_addr = cl->host_client_id;
mei_hdr.me_addr = cl->me_client_id;
mei_hdr.reserved = 0;
mei_hdr.internal = cb->internal;
rets = mei_write_message(dev, &mei_hdr, buf->data);

View File

@ -43,7 +43,7 @@ static ssize_t mei_dbgfs_read_meclients(struct file *fp, char __user *ubuf,
mutex_lock(&dev->device_lock);
/* if the driver is not enabled the list won't b consitent */
/* if the driver is not enabled the list won't be consistent */
if (dev->dev_state != MEI_DEV_ENABLED)
goto out;
@ -101,7 +101,7 @@ static const struct file_operations mei_dbgfs_fops_devstate = {
/**
* mei_dbgfs_deregister - Remove the debugfs files and directories
* @mei - pointer to mei device private dat
* @mei - pointer to mei device private data
*/
void mei_dbgfs_deregister(struct mei_device *dev)
{

View File

@ -28,9 +28,9 @@
*
* @dev: the device structure
*
* returns none.
* returns 0 on success -ENOMEM on allocation failure
*/
static void mei_hbm_me_cl_allocate(struct mei_device *dev)
static int mei_hbm_me_cl_allocate(struct mei_device *dev)
{
struct mei_me_client *clients;
int b;
@ -44,7 +44,7 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev)
dev->me_clients_num++;
if (dev->me_clients_num == 0)
return;
return 0;
kfree(dev->me_clients);
dev->me_clients = NULL;
@ -56,12 +56,10 @@ static void mei_hbm_me_cl_allocate(struct mei_device *dev)
sizeof(struct mei_me_client), GFP_KERNEL);
if (!clients) {
dev_err(&dev->pdev->dev, "memory allocation for ME clients failed.\n");
dev->dev_state = MEI_DEV_RESETTING;
mei_reset(dev, 1);
return;
return -ENOMEM;
}
dev->me_clients = clients;
return;
return 0;
}
/**
@ -85,12 +83,12 @@ void mei_hbm_cl_hdr(struct mei_cl *cl, u8 hbm_cmd, void *buf, size_t len)
}
/**
* same_disconn_addr - tells if they have the same address
* mei_hbm_cl_addr_equal - tells if they have the same address
*
* @file: private data of the file object.
* @disconn: disconnection request.
* @cl: - client
* @buf: buffer with cl header
*
* returns true if addres are same
* returns true if addresses are the same
*/
static inline
bool mei_hbm_cl_addr_equal(struct mei_cl *cl, void *buf)
@ -128,6 +126,17 @@ static bool is_treat_specially_client(struct mei_cl *cl,
return false;
}
/**
* mei_hbm_idle - set hbm to idle state
*
* @dev: the device structure
*/
void mei_hbm_idle(struct mei_device *dev)
{
dev->init_clients_timer = 0;
dev->hbm_state = MEI_HBM_IDLE;
}
int mei_hbm_start_wait(struct mei_device *dev)
{
int ret;
@ -137,7 +146,7 @@ int mei_hbm_start_wait(struct mei_device *dev)
mutex_unlock(&dev->device_lock);
ret = wait_event_interruptible_timeout(dev->wait_recvd_msg,
dev->hbm_state == MEI_HBM_IDLE ||
dev->hbm_state > MEI_HBM_START,
dev->hbm_state >= MEI_HBM_STARTED,
mei_secs_to_jiffies(MEI_INTEROP_TIMEOUT));
mutex_lock(&dev->device_lock);
@ -153,12 +162,15 @@ int mei_hbm_start_wait(struct mei_device *dev)
* mei_hbm_start_req - sends start request message.
*
* @dev: the device structure
*
* returns 0 on success and < 0 on failure
*/
int mei_hbm_start_req(struct mei_device *dev)
{
struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
struct hbm_host_version_request *start_req;
const size_t len = sizeof(struct hbm_host_version_request);
int ret;
mei_hbm_hdr(mei_hdr, len);
@ -170,12 +182,13 @@ int mei_hbm_start_req(struct mei_device *dev)
start_req->host_version.minor_version = HBM_MINOR_VERSION;
dev->hbm_state = MEI_HBM_IDLE;
if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
dev_err(&dev->pdev->dev, "version message write failed\n");
dev->dev_state = MEI_DEV_RESETTING;
mei_reset(dev, 1);
return -EIO;
ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
if (ret) {
dev_err(&dev->pdev->dev, "version message write failed: ret = %d\n",
ret);
return ret;
}
dev->hbm_state = MEI_HBM_START;
dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
return 0;
@ -186,13 +199,15 @@ int mei_hbm_start_req(struct mei_device *dev)
*
* @dev: the device structure
*
* returns none.
* returns 0 on success and < 0 on failure
*/
static void mei_hbm_enum_clients_req(struct mei_device *dev)
static int mei_hbm_enum_clients_req(struct mei_device *dev)
{
struct mei_msg_hdr *mei_hdr = &dev->wr_msg.hdr;
struct hbm_host_enum_request *enum_req;
const size_t len = sizeof(struct hbm_host_enum_request);
int ret;
/* enumerate clients */
mei_hbm_hdr(mei_hdr, len);
@ -200,14 +215,15 @@ static void mei_hbm_enum_clients_req(struct mei_device *dev)
memset(enum_req, 0, len);
enum_req->hbm_cmd = HOST_ENUM_REQ_CMD;
if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
dev->dev_state = MEI_DEV_RESETTING;
dev_err(&dev->pdev->dev, "enumeration request write failed.\n");
mei_reset(dev, 1);
ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
if (ret) {
dev_err(&dev->pdev->dev, "enumeration request write failed: ret = %d.\n",
ret);
return ret;
}
dev->hbm_state = MEI_HBM_ENUM_CLIENTS;
dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
return;
return 0;
}
/**
@ -215,7 +231,7 @@ static void mei_hbm_enum_clients_req(struct mei_device *dev)
*
* @dev: the device structure
*
* returns none.
* returns 0 on success and < 0 on failure
*/
static int mei_hbm_prop_req(struct mei_device *dev)
@ -226,7 +242,7 @@ static int mei_hbm_prop_req(struct mei_device *dev)
const size_t len = sizeof(struct hbm_props_request);
unsigned long next_client_index;
unsigned long client_num;
int ret;
client_num = dev->me_client_presentation_num;
@ -253,12 +269,11 @@ static int mei_hbm_prop_req(struct mei_device *dev)
prop_req->hbm_cmd = HOST_CLIENT_PROPERTIES_REQ_CMD;
prop_req->address = next_client_index;
if (mei_write_message(dev, mei_hdr, dev->wr_msg.data)) {
dev->dev_state = MEI_DEV_RESETTING;
dev_err(&dev->pdev->dev, "properties request write failed\n");
mei_reset(dev, 1);
return -EIO;
ret = mei_write_message(dev, mei_hdr, dev->wr_msg.data);
if (ret) {
dev_err(&dev->pdev->dev, "properties request write failed: ret = %d\n",
ret);
return ret;
}
dev->init_clients_timer = MEI_CLIENTS_INIT_TIMEOUT;
@ -268,7 +283,7 @@ static int mei_hbm_prop_req(struct mei_device *dev)
}
/**
* mei_hbm_stop_req_prepare - perpare stop request message
* mei_hbm_stop_req_prepare - prepare stop request message
*
* @dev - mei device
* @mei_hdr - mei message header
@ -289,7 +304,7 @@ static void mei_hbm_stop_req_prepare(struct mei_device *dev,
}
/**
* mei_hbm_cl_flow_control_req - sends flow control requst.
* mei_hbm_cl_flow_control_req - sends flow control request.
*
* @dev: the device structure
* @cl: client info
@ -451,7 +466,7 @@ int mei_hbm_cl_connect_req(struct mei_device *dev, struct mei_cl *cl)
}
/**
* mei_hbm_cl_connect_res - connect resposne from the ME
* mei_hbm_cl_connect_res - connect response from the ME
*
* @dev: the device structure
* @rs: connect response bus message
@ -505,8 +520,8 @@ static void mei_hbm_cl_connect_res(struct mei_device *dev,
/**
* mei_hbm_fw_disconnect_req - disconnect request initiated by me
* host sends disoconnect response
* mei_hbm_fw_disconnect_req - disconnect request initiated by ME firmware
* host sends disconnect response
*
* @dev: the device structure.
* @disconnect_req: disconnect request bus message from the me
@ -559,8 +574,10 @@ bool mei_hbm_version_is_supported(struct mei_device *dev)
*
* @dev: the device structure
* @mei_hdr: header of bus message
*
* returns 0 on success and < 0 on failure
*/
void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
{
struct mei_bus_message *mei_msg;
struct mei_me_client *me_client;
@ -577,8 +594,20 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
mei_read_slots(dev, dev->rd_msg_buf, hdr->length);
mei_msg = (struct mei_bus_message *)dev->rd_msg_buf;
/* ignore spurious message and prevent reset nesting
* hbm is put to idle during system reset
*/
if (dev->hbm_state == MEI_HBM_IDLE) {
dev_dbg(&dev->pdev->dev, "hbm: state is idle ignore spurious messages\n");
return 0;
}
switch (mei_msg->hbm_cmd) {
case HOST_START_RES_CMD:
dev_dbg(&dev->pdev->dev, "hbm: start: response message received.\n");
dev->init_clients_timer = 0;
version_res = (struct hbm_host_version_response *)mei_msg;
dev_dbg(&dev->pdev->dev, "HBM VERSION: DRIVER=%02d:%02d DEVICE=%02d:%02d\n",
@ -597,73 +626,89 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
}
if (!mei_hbm_version_is_supported(dev)) {
dev_warn(&dev->pdev->dev, "hbm version mismatch: stopping the driver.\n");
dev_warn(&dev->pdev->dev, "hbm: start: version mismatch - stopping the driver.\n");
dev->hbm_state = MEI_HBM_STOP;
dev->hbm_state = MEI_HBM_STOPPED;
mei_hbm_stop_req_prepare(dev, &dev->wr_msg.hdr,
dev->wr_msg.data);
mei_write_message(dev, &dev->wr_msg.hdr,
dev->wr_msg.data);
return;
if (mei_write_message(dev, &dev->wr_msg.hdr,
dev->wr_msg.data)) {
dev_err(&dev->pdev->dev, "hbm: start: failed to send stop request\n");
return -EIO;
}
break;
}
if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
dev->hbm_state == MEI_HBM_START) {
dev->init_clients_timer = 0;
mei_hbm_enum_clients_req(dev);
} else {
dev_err(&dev->pdev->dev, "reset: wrong host start response\n");
mei_reset(dev, 1);
return;
if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
dev->hbm_state != MEI_HBM_START) {
dev_err(&dev->pdev->dev, "hbm: start: state mismatch, [%d, %d]\n",
dev->dev_state, dev->hbm_state);
return -EPROTO;
}
dev->hbm_state = MEI_HBM_STARTED;
if (mei_hbm_enum_clients_req(dev)) {
dev_err(&dev->pdev->dev, "hbm: start: failed to send enumeration request\n");
return -EIO;
}
wake_up_interruptible(&dev->wait_recvd_msg);
dev_dbg(&dev->pdev->dev, "host start response message received.\n");
break;
case CLIENT_CONNECT_RES_CMD:
dev_dbg(&dev->pdev->dev, "hbm: client connect response: message received.\n");
connect_res = (struct hbm_client_connect_response *) mei_msg;
mei_hbm_cl_connect_res(dev, connect_res);
dev_dbg(&dev->pdev->dev, "client connect response message received.\n");
wake_up(&dev->wait_recvd_msg);
break;
case CLIENT_DISCONNECT_RES_CMD:
dev_dbg(&dev->pdev->dev, "hbm: client disconnect response: message received.\n");
disconnect_res = (struct hbm_client_connect_response *) mei_msg;
mei_hbm_cl_disconnect_res(dev, disconnect_res);
dev_dbg(&dev->pdev->dev, "client disconnect response message received.\n");
wake_up(&dev->wait_recvd_msg);
break;
case MEI_FLOW_CONTROL_CMD:
dev_dbg(&dev->pdev->dev, "hbm: client flow control response: message received.\n");
flow_control = (struct hbm_flow_control *) mei_msg;
mei_hbm_cl_flow_control_res(dev, flow_control);
dev_dbg(&dev->pdev->dev, "client flow control response message received.\n");
break;
case HOST_CLIENT_PROPERTIES_RES_CMD:
dev_dbg(&dev->pdev->dev, "hbm: properties response: message received.\n");
dev->init_clients_timer = 0;
if (dev->me_clients == NULL) {
dev_err(&dev->pdev->dev, "hbm: properties response: mei_clients not allocated\n");
return -EPROTO;
}
props_res = (struct hbm_props_response *)mei_msg;
me_client = &dev->me_clients[dev->me_client_presentation_num];
if (props_res->status || !dev->me_clients) {
dev_err(&dev->pdev->dev, "reset: properties response hbm wrong status.\n");
mei_reset(dev, 1);
return;
if (props_res->status) {
dev_err(&dev->pdev->dev, "hbm: properties response: wrong status = %d\n",
props_res->status);
return -EPROTO;
}
if (me_client->client_id != props_res->address) {
dev_err(&dev->pdev->dev, "reset: host properties response address mismatch\n");
mei_reset(dev, 1);
return;
dev_err(&dev->pdev->dev, "hbm: properties response: address mismatch %d ?= %d\n",
me_client->client_id, props_res->address);
return -EPROTO;
}
if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
dev->hbm_state != MEI_HBM_CLIENT_PROPERTIES) {
dev_err(&dev->pdev->dev, "reset: unexpected properties response\n");
mei_reset(dev, 1);
return;
dev_err(&dev->pdev->dev, "hbm: properties response: state mismatch, [%d, %d]\n",
dev->dev_state, dev->hbm_state);
return -EPROTO;
}
me_client->props = props_res->client_properties;
@ -671,49 +716,70 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
dev->me_client_presentation_num++;
/* request property for the next client */
mei_hbm_prop_req(dev);
if (mei_hbm_prop_req(dev))
return -EIO;
break;
case HOST_ENUM_RES_CMD:
dev_dbg(&dev->pdev->dev, "hbm: enumeration response: message received\n");
dev->init_clients_timer = 0;
enum_res = (struct hbm_host_enum_response *) mei_msg;
BUILD_BUG_ON(sizeof(dev->me_clients_map)
< sizeof(enum_res->valid_addresses));
memcpy(dev->me_clients_map, enum_res->valid_addresses,
sizeof(enum_res->valid_addresses));
if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
dev->hbm_state == MEI_HBM_ENUM_CLIENTS) {
dev->init_clients_timer = 0;
mei_hbm_me_cl_allocate(dev);
dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;
/* first property reqeust */
mei_hbm_prop_req(dev);
} else {
dev_err(&dev->pdev->dev, "reset: unexpected enumeration response hbm.\n");
mei_reset(dev, 1);
return;
if (dev->dev_state != MEI_DEV_INIT_CLIENTS ||
dev->hbm_state != MEI_HBM_ENUM_CLIENTS) {
dev_err(&dev->pdev->dev, "hbm: enumeration response: state mismatch, [%d, %d]\n",
dev->dev_state, dev->hbm_state);
return -EPROTO;
}
if (mei_hbm_me_cl_allocate(dev)) {
dev_err(&dev->pdev->dev, "hbm: enumeration response: cannot allocate clients array\n");
return -ENOMEM;
}
dev->hbm_state = MEI_HBM_CLIENT_PROPERTIES;
/* first property request */
if (mei_hbm_prop_req(dev))
return -EIO;
break;
case HOST_STOP_RES_CMD:
dev_dbg(&dev->pdev->dev, "hbm: stop response: message received\n");
if (dev->hbm_state != MEI_HBM_STOP)
dev_err(&dev->pdev->dev, "unexpected stop response hbm.\n");
dev->dev_state = MEI_DEV_DISABLED;
dev_info(&dev->pdev->dev, "reset: FW stop response.\n");
mei_reset(dev, 1);
dev->init_clients_timer = 0;
if (dev->hbm_state != MEI_HBM_STOPPED) {
dev_err(&dev->pdev->dev, "hbm: stop response: state mismatch, [%d, %d]\n",
dev->dev_state, dev->hbm_state);
return -EPROTO;
}
dev->dev_state = MEI_DEV_POWER_DOWN;
dev_info(&dev->pdev->dev, "hbm: stop response: resetting.\n");
/* force the reset */
return -EPROTO;
break;
case CLIENT_DISCONNECT_REQ_CMD:
/* search for client */
dev_dbg(&dev->pdev->dev, "hbm: disconnect request: message received\n");
disconnect_req = (struct hbm_client_connect_request *)mei_msg;
mei_hbm_fw_disconnect_req(dev, disconnect_req);
break;
case ME_STOP_REQ_CMD:
dev_dbg(&dev->pdev->dev, "hbm: stop request: message received\n");
dev->hbm_state = MEI_HBM_STOP;
dev->hbm_state = MEI_HBM_STOPPED;
mei_hbm_stop_req_prepare(dev, &dev->wr_ext_msg.hdr,
dev->wr_ext_msg.data);
break;
@ -722,5 +788,6 @@ void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr)
break;
}
return 0;
}

View File

@ -32,13 +32,13 @@ struct mei_cl;
enum mei_hbm_state {
MEI_HBM_IDLE = 0,
MEI_HBM_START,
MEI_HBM_STARTED,
MEI_HBM_ENUM_CLIENTS,
MEI_HBM_CLIENT_PROPERTIES,
MEI_HBM_STARTED,
MEI_HBM_STOP,
MEI_HBM_STOPPED,
};
void mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr);
int mei_hbm_dispatch(struct mei_device *dev, struct mei_msg_hdr *hdr);
static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length)
{
@ -49,6 +49,7 @@ static inline void mei_hbm_hdr(struct mei_msg_hdr *hdr, size_t length)
hdr->reserved = 0;
}
void mei_hbm_idle(struct mei_device *dev);
int mei_hbm_start_req(struct mei_device *dev);
int mei_hbm_start_wait(struct mei_device *dev);
int mei_hbm_cl_flow_control_req(struct mei_device *dev, struct mei_cl *cl);

View File

@ -185,7 +185,7 @@ static int mei_me_hw_reset(struct mei_device *dev, bool intr_enable)
mei_me_reg_write(hw, H_CSR, hcsr);
if (dev->dev_state == MEI_DEV_POWER_DOWN)
if (intr_enable == false)
mei_me_hw_reset_release(dev);
return 0;
@ -469,7 +469,7 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
struct mei_device *dev = (struct mei_device *) dev_id;
struct mei_cl_cb complete_list;
s32 slots;
int rets;
int rets = 0;
dev_dbg(&dev->pdev->dev, "function called after ISR to handle the interrupt processing.\n");
/* initialize our complete list */
@ -482,15 +482,10 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
mei_clear_interrupts(dev);
/* check if ME wants a reset */
if (!mei_hw_is_ready(dev) &&
dev->dev_state != MEI_DEV_RESETTING &&
dev->dev_state != MEI_DEV_INITIALIZING &&
dev->dev_state != MEI_DEV_POWER_DOWN &&
dev->dev_state != MEI_DEV_POWER_UP) {
dev_dbg(&dev->pdev->dev, "FW not ready.\n");
mei_reset(dev, 1);
mutex_unlock(&dev->device_lock);
return IRQ_HANDLED;
if (!mei_hw_is_ready(dev) && dev->dev_state != MEI_DEV_RESETTING) {
dev_warn(&dev->pdev->dev, "FW not ready: resetting.\n");
schedule_work(&dev->reset_work);
goto end;
}
/* check if we need to start the dev */
@ -500,15 +495,12 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
dev->recvd_hw_ready = true;
wake_up_interruptible(&dev->wait_hw_ready);
mutex_unlock(&dev->device_lock);
return IRQ_HANDLED;
} else {
dev_dbg(&dev->pdev->dev, "Reset Completed.\n");
mei_me_hw_reset_release(dev);
mutex_unlock(&dev->device_lock);
return IRQ_HANDLED;
}
goto end;
}
/* check slots available for reading */
slots = mei_count_full_read_slots(dev);
@ -516,21 +508,23 @@ irqreturn_t mei_me_irq_thread_handler(int irq, void *dev_id)
/* we have urgent data to send so break the read */
if (dev->wr_ext_msg.hdr.length)
break;
dev_dbg(&dev->pdev->dev, "slots =%08x\n", slots);
dev_dbg(&dev->pdev->dev, "call mei_irq_read_handler.\n");
dev_dbg(&dev->pdev->dev, "slots to read = %08x\n", slots);
rets = mei_irq_read_handler(dev, &complete_list, &slots);
if (rets)
if (rets && dev->dev_state != MEI_DEV_RESETTING) {
schedule_work(&dev->reset_work);
goto end;
}
}
rets = mei_irq_write_handler(dev, &complete_list);
end:
dev_dbg(&dev->pdev->dev, "end of bottom half function.\n");
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
mutex_unlock(&dev->device_lock);
rets = mei_irq_write_handler(dev, &complete_list);
dev->hbuf_is_ready = mei_hbuf_is_ready(dev);
mei_irq_compl_handler(dev, &complete_list);
end:
dev_dbg(&dev->pdev->dev, "interrupt thread end ret = %d\n", rets);
mutex_unlock(&dev->device_lock);
return IRQ_HANDLED;
}
static const struct mei_hw_ops mei_me_hw_ops = {

View File

@ -111,7 +111,8 @@ struct mei_msg_hdr {
u32 me_addr:8;
u32 host_addr:8;
u32 length:9;
u32 reserved:6;
u32 reserved:5;
u32 internal:1;
u32 msg_complete:1;
} __packed;

View File

@ -43,41 +43,119 @@ const char *mei_dev_state_str(int state)
#undef MEI_DEV_STATE
}
void mei_device_init(struct mei_device *dev)
/**
* mei_cancel_work. Cancel mei background jobs
*
* @dev: the device structure
*
* returns 0 on success or < 0 if the reset hasn't succeeded
*/
void mei_cancel_work(struct mei_device *dev)
{
/* setup our list array */
INIT_LIST_HEAD(&dev->file_list);
INIT_LIST_HEAD(&dev->device_list);
mutex_init(&dev->device_lock);
init_waitqueue_head(&dev->wait_hw_ready);
init_waitqueue_head(&dev->wait_recvd_msg);
init_waitqueue_head(&dev->wait_stop_wd);
dev->dev_state = MEI_DEV_INITIALIZING;
cancel_work_sync(&dev->init_work);
cancel_work_sync(&dev->reset_work);
mei_io_list_init(&dev->read_list);
mei_io_list_init(&dev->write_list);
mei_io_list_init(&dev->write_waiting_list);
mei_io_list_init(&dev->ctrl_wr_list);
mei_io_list_init(&dev->ctrl_rd_list);
INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
INIT_WORK(&dev->init_work, mei_host_client_init);
INIT_LIST_HEAD(&dev->wd_cl.link);
INIT_LIST_HEAD(&dev->iamthif_cl.link);
mei_io_list_init(&dev->amthif_cmd_list);
mei_io_list_init(&dev->amthif_rd_complete_list);
bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
dev->open_handle_count = 0;
/*
* Reserving the first client ID
* 0: Reserved for MEI Bus Message communications
*/
bitmap_set(dev->host_clients_map, 0, 1);
cancel_delayed_work(&dev->timer_work);
}
EXPORT_SYMBOL_GPL(mei_device_init);
EXPORT_SYMBOL_GPL(mei_cancel_work);
/**
* mei_reset - resets host and fw.
*
* @dev: the device structure
*/
int mei_reset(struct mei_device *dev)
{
enum mei_dev_state state = dev->dev_state;
bool interrupts_enabled;
int ret;
if (state != MEI_DEV_INITIALIZING &&
state != MEI_DEV_DISABLED &&
state != MEI_DEV_POWER_DOWN &&
state != MEI_DEV_POWER_UP)
dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
mei_dev_state_str(state));
/* we're already in reset, cancel the init timer
* if the reset was called due the hbm protocol error
* we need to call it before hw start
* so the hbm watchdog won't kick in
*/
mei_hbm_idle(dev);
/* enter reset flow */
interrupts_enabled = state != MEI_DEV_POWER_DOWN;
dev->dev_state = MEI_DEV_RESETTING;
dev->reset_count++;
if (dev->reset_count > MEI_MAX_CONSEC_RESET) {
dev_err(&dev->pdev->dev, "reset: reached maximal consecutive resets: disabling the device\n");
dev->dev_state = MEI_DEV_DISABLED;
return -ENODEV;
}
ret = mei_hw_reset(dev, interrupts_enabled);
/* fall through and remove the sw state even if hw reset has failed */
/* no need to clean up software state in case of power up */
if (state != MEI_DEV_INITIALIZING &&
state != MEI_DEV_POWER_UP) {
/* remove all waiting requests */
mei_cl_all_write_clear(dev);
mei_cl_all_disconnect(dev);
/* wake up all readers and writers so they can be interrupted */
mei_cl_all_wakeup(dev);
/* remove entry if already in list */
dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n");
mei_cl_unlink(&dev->wd_cl);
mei_cl_unlink(&dev->iamthif_cl);
mei_amthif_reset_params(dev);
memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
}
dev->me_clients_num = 0;
dev->rd_msg_hdr = 0;
dev->wd_pending = false;
if (ret) {
dev_err(&dev->pdev->dev, "hw_reset failed ret = %d\n", ret);
dev->dev_state = MEI_DEV_DISABLED;
return ret;
}
if (state == MEI_DEV_POWER_DOWN) {
dev_dbg(&dev->pdev->dev, "powering down: end of reset\n");
dev->dev_state = MEI_DEV_DISABLED;
return 0;
}
ret = mei_hw_start(dev);
if (ret) {
dev_err(&dev->pdev->dev, "hw_start failed ret = %d\n", ret);
dev->dev_state = MEI_DEV_DISABLED;
return ret;
}
dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
dev->dev_state = MEI_DEV_INIT_CLIENTS;
ret = mei_hbm_start_req(dev);
if (ret) {
dev_err(&dev->pdev->dev, "hbm_start failed ret = %d\n", ret);
dev->dev_state = MEI_DEV_DISABLED;
return ret;
}
return 0;
}
EXPORT_SYMBOL_GPL(mei_reset);
/**
* mei_start - initializes host and fw to start work.
@ -90,14 +168,21 @@ int mei_start(struct mei_device *dev)
{
mutex_lock(&dev->device_lock);
/* acknowledge interrupt and stop interupts */
/* acknowledge interrupt and stop interrupts */
mei_clear_interrupts(dev);
mei_hw_config(dev);
dev_dbg(&dev->pdev->dev, "reset in start the mei device.\n");
mei_reset(dev, 1);
dev->dev_state = MEI_DEV_INITIALIZING;
dev->reset_count = 0;
mei_reset(dev);
if (dev->dev_state == MEI_DEV_DISABLED) {
dev_err(&dev->pdev->dev, "reset failed");
goto err;
}
if (mei_hbm_start_wait(dev)) {
dev_err(&dev->pdev->dev, "HBM haven't started");
@ -132,101 +217,64 @@ err:
EXPORT_SYMBOL_GPL(mei_start);
/**
* mei_reset - resets host and fw.
* mei_restart - restart device after suspend
*
* @dev: the device structure
* @interrupts_enabled: if interrupt should be enabled after reset.
*
* returns 0 on success or -ENODEV if the restart hasn't succeeded
*/
void mei_reset(struct mei_device *dev, int interrupts_enabled)
int mei_restart(struct mei_device *dev)
{
bool unexpected;
int ret;
int err;
unexpected = (dev->dev_state != MEI_DEV_INITIALIZING &&
dev->dev_state != MEI_DEV_DISABLED &&
dev->dev_state != MEI_DEV_POWER_DOWN &&
dev->dev_state != MEI_DEV_POWER_UP);
mutex_lock(&dev->device_lock);
if (unexpected)
dev_warn(&dev->pdev->dev, "unexpected reset: dev_state = %s\n",
mei_dev_state_str(dev->dev_state));
mei_clear_interrupts(dev);
ret = mei_hw_reset(dev, interrupts_enabled);
if (ret) {
dev_err(&dev->pdev->dev, "hw reset failed disabling the device\n");
interrupts_enabled = false;
dev->dev_state = MEI_DEV_DISABLED;
}
dev->dev_state = MEI_DEV_POWER_UP;
dev->reset_count = 0;
dev->hbm_state = MEI_HBM_IDLE;
err = mei_reset(dev);
if (dev->dev_state != MEI_DEV_INITIALIZING &&
dev->dev_state != MEI_DEV_POWER_UP) {
if (dev->dev_state != MEI_DEV_DISABLED &&
dev->dev_state != MEI_DEV_POWER_DOWN)
dev->dev_state = MEI_DEV_RESETTING;
mutex_unlock(&dev->device_lock);
/* remove all waiting requests */
mei_cl_all_write_clear(dev);
mei_cl_all_disconnect(dev);
/* wake up all readings so they can be interrupted */
mei_cl_all_wakeup(dev);
/* remove entry if already in list */
dev_dbg(&dev->pdev->dev, "remove iamthif and wd from the file list.\n");
mei_cl_unlink(&dev->wd_cl);
mei_cl_unlink(&dev->iamthif_cl);
mei_amthif_reset_params(dev);
memset(&dev->wr_ext_msg, 0, sizeof(dev->wr_ext_msg));
}
/* we're already in reset, cancel the init timer */
dev->init_clients_timer = 0;
dev->me_clients_num = 0;
dev->rd_msg_hdr = 0;
dev->wd_pending = false;
if (!interrupts_enabled) {
dev_dbg(&dev->pdev->dev, "intr not enabled end of reset\n");
return;
}
ret = mei_hw_start(dev);
if (ret) {
dev_err(&dev->pdev->dev, "hw_start failed disabling the device\n");
dev->dev_state = MEI_DEV_DISABLED;
return;
}
dev_dbg(&dev->pdev->dev, "link is established start sending messages.\n");
/* link is established * start sending messages. */
dev->dev_state = MEI_DEV_INIT_CLIENTS;
mei_hbm_start_req(dev);
if (err || dev->dev_state == MEI_DEV_DISABLED)
return -ENODEV;
return 0;
}
EXPORT_SYMBOL_GPL(mei_restart);
static void mei_reset_work(struct work_struct *work)
{
struct mei_device *dev =
container_of(work, struct mei_device, reset_work);
mutex_lock(&dev->device_lock);
mei_reset(dev);
mutex_unlock(&dev->device_lock);
if (dev->dev_state == MEI_DEV_DISABLED)
dev_err(&dev->pdev->dev, "reset failed");
}
EXPORT_SYMBOL_GPL(mei_reset);
void mei_stop(struct mei_device *dev)
{
dev_dbg(&dev->pdev->dev, "stopping the device.\n");
flush_scheduled_work();
mei_cancel_work(dev);
mei_nfc_host_exit(dev);
mutex_lock(&dev->device_lock);
cancel_delayed_work(&dev->timer_work);
mei_wd_stop(dev);
mei_nfc_host_exit();
dev->dev_state = MEI_DEV_POWER_DOWN;
mei_reset(dev, 0);
mei_reset(dev);
mutex_unlock(&dev->device_lock);
@ -236,3 +284,41 @@ EXPORT_SYMBOL_GPL(mei_stop);
void mei_device_init(struct mei_device *dev)
{
/* setup our list array */
INIT_LIST_HEAD(&dev->file_list);
INIT_LIST_HEAD(&dev->device_list);
mutex_init(&dev->device_lock);
init_waitqueue_head(&dev->wait_hw_ready);
init_waitqueue_head(&dev->wait_recvd_msg);
init_waitqueue_head(&dev->wait_stop_wd);
dev->dev_state = MEI_DEV_INITIALIZING;
dev->reset_count = 0;
mei_io_list_init(&dev->read_list);
mei_io_list_init(&dev->write_list);
mei_io_list_init(&dev->write_waiting_list);
mei_io_list_init(&dev->ctrl_wr_list);
mei_io_list_init(&dev->ctrl_rd_list);
INIT_DELAYED_WORK(&dev->timer_work, mei_timer);
INIT_WORK(&dev->init_work, mei_host_client_init);
INIT_WORK(&dev->reset_work, mei_reset_work);
INIT_LIST_HEAD(&dev->wd_cl.link);
INIT_LIST_HEAD(&dev->iamthif_cl.link);
mei_io_list_init(&dev->amthif_cmd_list);
mei_io_list_init(&dev->amthif_rd_complete_list);
bitmap_zero(dev->host_clients_map, MEI_CLIENTS_MAX);
dev->open_handle_count = 0;
/*
* Reserving the first client ID
* 0: Reserved for MEI Bus Message communications
*/
bitmap_set(dev->host_clients_map, 0, 1);
}
EXPORT_SYMBOL_GPL(mei_device_init);

View File

@ -31,7 +31,7 @@
/**
* mei_irq_compl_handler - dispatch complete handelers
* mei_irq_compl_handler - dispatch complete handlers
* for the completed callbacks
*
* @dev - mei device
@ -301,13 +301,11 @@ int mei_irq_read_handler(struct mei_device *dev,
struct mei_cl_cb *cmpl_list, s32 *slots)
{
struct mei_msg_hdr *mei_hdr;
struct mei_cl *cl_pos = NULL;
struct mei_cl *cl_next = NULL;
int ret = 0;
struct mei_cl *cl;
int ret;
if (!dev->rd_msg_hdr) {
dev->rd_msg_hdr = mei_read_hdr(dev);
dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
(*slots)--;
dev_dbg(&dev->pdev->dev, "slots =%08x.\n", *slots);
}
@ -315,61 +313,67 @@ int mei_irq_read_handler(struct mei_device *dev,
dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
if (mei_hdr->reserved || !dev->rd_msg_hdr) {
dev_dbg(&dev->pdev->dev, "corrupted message header.\n");
dev_err(&dev->pdev->dev, "corrupted message header 0x%08X\n",
dev->rd_msg_hdr);
ret = -EBADMSG;
goto end;
}
if (mei_hdr->host_addr || mei_hdr->me_addr) {
list_for_each_entry_safe(cl_pos, cl_next,
&dev->file_list, link) {
dev_dbg(&dev->pdev->dev,
"list_for_each_entry_safe read host"
" client = %d, ME client = %d\n",
cl_pos->host_client_id,
cl_pos->me_client_id);
if (mei_cl_hbm_equal(cl_pos, mei_hdr))
break;
}
if (&cl_pos->link == &dev->file_list) {
dev_dbg(&dev->pdev->dev, "corrupted message header\n");
ret = -EBADMSG;
goto end;
}
}
if (((*slots) * sizeof(u32)) < mei_hdr->length) {
dev_err(&dev->pdev->dev,
"we can't read the message slots =%08x.\n",
if (mei_slots2data(*slots) < mei_hdr->length) {
dev_err(&dev->pdev->dev, "less data available than length=%08x.\n",
*slots);
/* we can't read the message */
ret = -ERANGE;
goto end;
}
/* decide where to read the message too */
if (!mei_hdr->host_addr) {
dev_dbg(&dev->pdev->dev, "call mei_hbm_dispatch.\n");
mei_hbm_dispatch(dev, mei_hdr);
dev_dbg(&dev->pdev->dev, "end mei_hbm_dispatch.\n");
} else if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
(MEI_FILE_CONNECTED == dev->iamthif_cl.state) &&
(dev->iamthif_state == MEI_IAMTHIF_READING)) {
dev_dbg(&dev->pdev->dev, "call mei_amthif_irq_read_msg.\n");
dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list);
if (ret)
goto end;
} else {
dev_dbg(&dev->pdev->dev, "call mei_cl_irq_read_msg.\n");
dev_dbg(&dev->pdev->dev, MEI_HDR_FMT, MEI_HDR_PRM(mei_hdr));
ret = mei_cl_irq_read_msg(dev, mei_hdr, cmpl_list);
if (ret)
/* HBM message */
if (mei_hdr->host_addr == 0 && mei_hdr->me_addr == 0) {
ret = mei_hbm_dispatch(dev, mei_hdr);
if (ret) {
dev_dbg(&dev->pdev->dev, "mei_hbm_dispatch failed ret = %d\n",
ret);
goto end;
}
goto reset_slots;
}
/* find recipient cl */
list_for_each_entry(cl, &dev->file_list, link) {
if (mei_cl_hbm_equal(cl, mei_hdr)) {
cl_dbg(dev, cl, "got a message\n");
break;
}
}
/* if no recipient cl was found we assume corrupted header */
if (&cl->link == &dev->file_list) {
dev_err(&dev->pdev->dev, "no destination client found 0x%08X\n",
dev->rd_msg_hdr);
ret = -EBADMSG;
goto end;
}
if (mei_hdr->host_addr == dev->iamthif_cl.host_client_id &&
MEI_FILE_CONNECTED == dev->iamthif_cl.state &&
dev->iamthif_state == MEI_IAMTHIF_READING) {
ret = mei_amthif_irq_read_msg(dev, mei_hdr, cmpl_list);
if (ret) {
dev_err(&dev->pdev->dev, "mei_amthif_irq_read_msg failed = %d\n",
ret);
goto end;
}
} else {
ret = mei_cl_irq_read_msg(dev, mei_hdr, cmpl_list);
if (ret) {
dev_err(&dev->pdev->dev, "mei_cl_irq_read_msg failed = %d\n",
ret);
goto end;
}
}
reset_slots:
/* reset the number of slots and header */
*slots = mei_count_full_read_slots(dev);
dev->rd_msg_hdr = 0;
@ -533,7 +537,6 @@ EXPORT_SYMBOL_GPL(mei_irq_write_handler);
*
* @work: pointer to the work_struct structure
*
* NOTE: This function is called by timer interrupt work
*/
void mei_timer(struct work_struct *work)
{
@ -548,24 +551,30 @@ void mei_timer(struct work_struct *work)
mutex_lock(&dev->device_lock);
if (dev->dev_state != MEI_DEV_ENABLED) {
if (dev->dev_state == MEI_DEV_INIT_CLIENTS) {
if (dev->init_clients_timer) {
if (--dev->init_clients_timer == 0) {
dev_err(&dev->pdev->dev, "reset: init clients timeout hbm_state = %d.\n",
dev->hbm_state);
mei_reset(dev, 1);
}
/* Catch interrupt stalls during HBM init handshake */
if (dev->dev_state == MEI_DEV_INIT_CLIENTS &&
dev->hbm_state != MEI_HBM_IDLE) {
if (dev->init_clients_timer) {
if (--dev->init_clients_timer == 0) {
dev_err(&dev->pdev->dev, "timer: init clients timeout hbm_state = %d.\n",
dev->hbm_state);
mei_reset(dev);
goto out;
}
}
goto out;
}
if (dev->dev_state != MEI_DEV_ENABLED)
goto out;
/*** connect/disconnect timeouts ***/
list_for_each_entry_safe(cl_pos, cl_next, &dev->file_list, link) {
if (cl_pos->timer_count) {
if (--cl_pos->timer_count == 0) {
dev_err(&dev->pdev->dev, "reset: connect/disconnect timeout.\n");
mei_reset(dev, 1);
dev_err(&dev->pdev->dev, "timer: connect/disconnect timeout.\n");
mei_reset(dev);
goto out;
}
}
@ -573,8 +582,8 @@ void mei_timer(struct work_struct *work)
if (dev->iamthif_stall_timer) {
if (--dev->iamthif_stall_timer == 0) {
dev_err(&dev->pdev->dev, "reset: amthif hanged.\n");
mei_reset(dev, 1);
dev_err(&dev->pdev->dev, "timer: amthif hanged.\n");
mei_reset(dev);
dev->iamthif_msg_buf_size = 0;
dev->iamthif_msg_buf_index = 0;
dev->iamthif_canceled = false;
@ -627,7 +636,8 @@ void mei_timer(struct work_struct *work)
}
}
out:
schedule_delayed_work(&dev->timer_work, 2 * HZ);
if (dev->dev_state != MEI_DEV_DISABLED)
schedule_delayed_work(&dev->timer_work, 2 * HZ);
mutex_unlock(&dev->device_lock);
}

View File

@ -48,7 +48,7 @@
*
* @inode: pointer to inode structure
* @file: pointer to file structure
e
*
* returns 0 on success, <0 on error
*/
static int mei_open(struct inode *inode, struct file *file)

View File

@ -60,12 +60,17 @@ extern const uuid_le mei_wd_guid;
*/
#define MEI_CLIENTS_MAX 256
/*
* maximum number of consecutive resets
*/
#define MEI_MAX_CONSEC_RESET 3
/*
* Number of File descriptors/handles
* that can be opened to the driver.
*
* Limit to 255: 256 Total Clients
* minus internal client for MEI Bus Messags
* minus internal client for MEI Bus Messages
*/
#define MEI_MAX_OPEN_HANDLE_COUNT (MEI_CLIENTS_MAX - 1)
@ -178,9 +183,10 @@ struct mei_cl_cb {
unsigned long buf_idx;
unsigned long read_time;
struct file *file_object;
u32 internal:1;
};
/* MEI client instance carried as file->pirvate_data*/
/* MEI client instance carried as file->private_data*/
struct mei_cl {
struct list_head link;
struct mei_device *dev;
@ -326,6 +332,7 @@ struct mei_cl_device {
/**
* struct mei_device - MEI private device struct
* @reset_count - limits the number of consecutive resets
* @hbm_state - state of host bus message protocol
* @mem_addr - mem mapped base register address
@ -369,6 +376,7 @@ struct mei_device {
/*
* mei device states
*/
unsigned long reset_count;
enum mei_dev_state dev_state;
enum mei_hbm_state hbm_state;
u16 init_clients_timer;
@ -427,6 +435,7 @@ struct mei_device {
bool iamthif_canceled;
struct work_struct init_work;
struct work_struct reset_work;
/* List of bus devices */
struct list_head device_list;
@ -456,13 +465,25 @@ static inline u32 mei_data2slots(size_t length)
return DIV_ROUND_UP(sizeof(struct mei_msg_hdr) + length, 4);
}
/**
* mei_slots2data- get data in slots - bytes from slots
* @slots - number of available slots
* returns - number of bytes in slots
*/
static inline u32 mei_slots2data(int slots)
{
return slots * 4;
}
/*
* mei init function prototypes
*/
void mei_device_init(struct mei_device *dev);
void mei_reset(struct mei_device *dev, int interrupts);
int mei_reset(struct mei_device *dev);
int mei_start(struct mei_device *dev);
int mei_restart(struct mei_device *dev);
void mei_stop(struct mei_device *dev);
void mei_cancel_work(struct mei_device *dev);
/*
* MEI interrupt functions prototype
@ -510,7 +531,7 @@ int mei_amthif_irq_read(struct mei_device *dev, s32 *slots);
* NFC functions
*/
int mei_nfc_host_init(struct mei_device *dev);
void mei_nfc_host_exit(void);
void mei_nfc_host_exit(struct mei_device *dev);
/*
* NFC Client UUID
@ -626,9 +647,9 @@ static inline void mei_dbgfs_deregister(struct mei_device *dev) {}
int mei_register(struct mei_device *dev);
void mei_deregister(struct mei_device *dev);
#define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d comp=%1d"
#define MEI_HDR_FMT "hdr:host=%02d me=%02d len=%d internal=%1d comp=%1d"
#define MEI_HDR_PRM(hdr) \
(hdr)->host_addr, (hdr)->me_addr, \
(hdr)->length, (hdr)->msg_complete
(hdr)->length, (hdr)->internal, (hdr)->msg_complete
#endif

View File

@ -92,7 +92,7 @@ struct mei_nfc_hci_hdr {
* @cl: NFC host client
* @cl_info: NFC info host client
* @init_work: perform connection to the info client
* @fw_ivn: NFC Intervace Version Number
* @fw_ivn: NFC Interface Version Number
* @vendor_id: NFC manufacturer ID
* @radio_type: NFC radio type
*/
@ -163,7 +163,7 @@ static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev)
return 0;
default:
dev_err(&dev->pdev->dev, "Unknow radio type 0x%x\n",
dev_err(&dev->pdev->dev, "Unknown radio type 0x%x\n",
ndev->radio_type);
return -EINVAL;
@ -175,14 +175,14 @@ static int mei_nfc_build_bus_name(struct mei_nfc_dev *ndev)
ndev->bus_name = "pn544";
return 0;
default:
dev_err(&dev->pdev->dev, "Unknow radio type 0x%x\n",
dev_err(&dev->pdev->dev, "Unknown radio type 0x%x\n",
ndev->radio_type);
return -EINVAL;
}
default:
dev_err(&dev->pdev->dev, "Unknow vendor ID 0x%x\n",
dev_err(&dev->pdev->dev, "Unknown vendor ID 0x%x\n",
ndev->vendor_id);
return -EINVAL;
@ -428,7 +428,7 @@ static void mei_nfc_init(struct work_struct *work)
mutex_unlock(&dev->device_lock);
if (mei_nfc_if_version(ndev) < 0) {
dev_err(&dev->pdev->dev, "Could not get the NFC interfave version");
dev_err(&dev->pdev->dev, "Could not get the NFC interface version");
goto err;
}
@ -469,7 +469,9 @@ static void mei_nfc_init(struct work_struct *work)
return;
err:
mutex_lock(&dev->device_lock);
mei_nfc_free(ndev);
mutex_unlock(&dev->device_lock);
return;
}
@ -481,7 +483,7 @@ int mei_nfc_host_init(struct mei_device *dev)
struct mei_cl *cl_info, *cl = NULL;
int i, ret;
/* already initialzed */
/* already initialized */
if (ndev->cl_info)
return 0;
@ -547,12 +549,16 @@ err:
return ret;
}
void mei_nfc_host_exit(void)
void mei_nfc_host_exit(struct mei_device *dev)
{
struct mei_nfc_dev *ndev = &nfc_dev;
cancel_work_sync(&ndev->init_work);
mutex_lock(&dev->device_lock);
if (ndev->cl && ndev->cl->device)
mei_cl_remove_device(ndev->cl->device);
mei_nfc_free(ndev);
mutex_unlock(&dev->device_lock);
}

View File

@ -144,6 +144,21 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
dev_err(&pdev->dev, "failed to get pci regions.\n");
goto disable_device;
}
if (dma_set_mask(&pdev->dev, DMA_BIT_MASK(64)) ||
dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64))) {
err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
if (err)
err = dma_set_coherent_mask(&pdev->dev,
DMA_BIT_MASK(32));
}
if (err) {
dev_err(&pdev->dev, "No usable DMA configuration, aborting\n");
goto release_regions;
}
/* allocates and initializes the mei dev structure */
dev = mei_me_dev_init(pdev);
if (!dev) {
@ -197,8 +212,8 @@ static int mei_me_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
return 0;
release_irq:
mei_cancel_work(dev);
mei_disable_interrupts(dev);
flush_scheduled_work();
free_irq(pdev->irq, dev);
disable_msi:
pci_disable_msi(pdev);
@ -306,16 +321,14 @@ static int mei_me_pci_resume(struct device *device)
return err;
}
mutex_lock(&dev->device_lock);
dev->dev_state = MEI_DEV_POWER_UP;
mei_clear_interrupts(dev);
mei_reset(dev, 1);
mutex_unlock(&dev->device_lock);
err = mei_restart(dev);
if (err)
return err;
/* Start timer if stopped in suspend */
schedule_delayed_work(&dev->timer_work, HZ);
return err;
return 0;
}
static SIMPLE_DEV_PM_OPS(mei_me_pm_ops, mei_me_pci_suspend, mei_me_pci_resume);
#define MEI_ME_PM_OPS (&mei_me_pm_ops)

View File

@ -115,6 +115,7 @@ int mei_wd_send(struct mei_device *dev)
hdr.me_addr = dev->wd_cl.me_client_id;
hdr.msg_complete = 1;
hdr.reserved = 0;
hdr.internal = 0;
if (!memcmp(dev->wd_data, mei_start_wd_params, MEI_WD_HDR_SIZE))
hdr.length = MEI_WD_START_MSG_SIZE;

View File

@ -134,6 +134,8 @@ struct mic_device {
* @send_intr: Send an interrupt for a particular doorbell on the card.
* @ack_interrupt: Hardware specific operations to ack the h/w on
* receipt of an interrupt.
* @intr_workarounds: Hardware specific workarounds needed after
* handling an interrupt.
* @reset: Reset the remote processor.
* @reset_fw_ready: Reset firmware ready field.
* @is_fw_ready: Check if firmware is ready for OS download.
@ -149,6 +151,7 @@ struct mic_hw_ops {
void (*write_spad)(struct mic_device *mdev, unsigned int idx, u32 val);
void (*send_intr)(struct mic_device *mdev, int doorbell);
u32 (*ack_interrupt)(struct mic_device *mdev);
void (*intr_workarounds)(struct mic_device *mdev);
void (*reset)(struct mic_device *mdev);
void (*reset_fw_ready)(struct mic_device *mdev);
bool (*is_fw_ready)(struct mic_device *mdev);

View File

@ -115,7 +115,7 @@ static irqreturn_t mic_shutdown_db(int irq, void *data)
struct mic_device *mdev = data;
struct mic_bootparam *bootparam = mdev->dp;
mdev->ops->ack_interrupt(mdev);
mdev->ops->intr_workarounds(mdev);
switch (bootparam->shutdown_status) {
case MIC_HALTED:

View File

@ -369,7 +369,7 @@ static irqreturn_t mic_virtio_intr_handler(int irq, void *data)
struct mic_vdev *mvdev = data;
struct mic_device *mdev = mvdev->mdev;
mdev->ops->ack_interrupt(mdev);
mdev->ops->intr_workarounds(mdev);
schedule_work(&mvdev->virtio_bh_work);
return IRQ_HANDLED;
}

View File

@ -174,35 +174,38 @@ static void mic_x100_send_intr(struct mic_device *mdev, int doorbell)
}
/**
* mic_ack_interrupt - Device specific interrupt handling.
* @mdev: pointer to mic_device instance
* mic_x100_ack_interrupt - Read the interrupt sources register and
* clear it. This function will be called in the MSI/INTx case.
* @mdev: Pointer to mic_device instance.
*
* Returns: bitmask of doorbell events triggered.
* Returns: bitmask of interrupt sources triggered.
*/
static u32 mic_x100_ack_interrupt(struct mic_device *mdev)
{
u32 reg = 0;
struct mic_mw *mw = &mdev->mmio;
u32 sicr0 = MIC_X100_SBOX_BASE_ADDRESS + MIC_X100_SBOX_SICR0;
u32 reg = mic_mmio_read(&mdev->mmio, sicr0);
mic_mmio_write(&mdev->mmio, reg, sicr0);
return reg;
}
/**
* mic_x100_intr_workarounds - These hardware specific workarounds are
* to be invoked everytime an interrupt is handled.
* @mdev: Pointer to mic_device instance.
*
* Returns: none
*/
static void mic_x100_intr_workarounds(struct mic_device *mdev)
{
struct mic_mw *mw = &mdev->mmio;
/* Clear pending bit array. */
if (MIC_A0_STEP == mdev->stepping)
mic_mmio_write(mw, 1, MIC_X100_SBOX_BASE_ADDRESS +
MIC_X100_SBOX_MSIXPBACR);
if (mdev->irq_info.num_vectors <= 1) {
reg = mic_mmio_read(mw, sicr0);
if (unlikely(!reg))
goto done;
mic_mmio_write(mw, reg, sicr0);
}
if (mdev->stepping >= MIC_B0_STEP)
mdev->intr_ops->enable_interrupts(mdev);
done:
return reg;
}
/**
@ -553,6 +556,7 @@ struct mic_hw_ops mic_x100_ops = {
.write_spad = mic_x100_write_spad,
.send_intr = mic_x100_send_intr,
.ack_interrupt = mic_x100_ack_interrupt,
.intr_workarounds = mic_x100_intr_workarounds,
.reset = mic_x100_hw_reset,
.reset_fw_ready = mic_x100_reset_fw_ready,
.is_fw_ready = mic_x100_is_fw_ready,

View File

@ -828,6 +828,7 @@ enum xp_retval
xpc_allocate_msg_wait(struct xpc_channel *ch)
{
enum xp_retval ret;
DEFINE_WAIT(wait);
if (ch->flags & XPC_C_DISCONNECTING) {
DBUG_ON(ch->reason == xpInterrupted);
@ -835,7 +836,9 @@ xpc_allocate_msg_wait(struct xpc_channel *ch)
}
atomic_inc(&ch->n_on_msg_allocate_wq);
ret = interruptible_sleep_on_timeout(&ch->msg_allocate_wq, 1);
prepare_to_wait(&ch->msg_allocate_wq, &wait, TASK_INTERRUPTIBLE);
ret = schedule_timeout(1);
finish_wait(&ch->msg_allocate_wq, &wait);
atomic_dec(&ch->n_on_msg_allocate_wq);
if (ch->flags & XPC_C_DISCONNECTING) {

View File

@ -812,7 +812,7 @@ static void st_tty_flush_buffer(struct tty_struct *tty)
kfree_skb(st_gdata->tx_skb);
st_gdata->tx_skb = NULL;
tty->ops->flush_buffer(tty);
tty_driver_flush_buffer(tty);
return;
}

View File

@ -531,7 +531,6 @@ long st_kim_stop(void *kim_data)
/* Flush any pending characters in the driver and discipline. */
tty_ldisc_flush(tty);
tty_driver_flush_buffer(tty);
tty->ops->flush_buffer(tty);
}
/* send uninstall notification to UIM */

View File

@ -165,7 +165,7 @@ static void vmci_guest_cid_update(u32 sub_id,
* true if required hypercalls (or fallback hypercalls) are
* supported by the host, false otherwise.
*/
static bool vmci_check_host_caps(struct pci_dev *pdev)
static int vmci_check_host_caps(struct pci_dev *pdev)
{
bool result;
struct vmci_resource_query_msg *msg;
@ -176,7 +176,7 @@ static bool vmci_check_host_caps(struct pci_dev *pdev)
check_msg = kmalloc(msg_size, GFP_KERNEL);
if (!check_msg) {
dev_err(&pdev->dev, "%s: Insufficient memory\n", __func__);
return false;
return -ENOMEM;
}
check_msg->dst = vmci_make_handle(VMCI_HYPERVISOR_CONTEXT_ID,
@ -196,7 +196,7 @@ static bool vmci_check_host_caps(struct pci_dev *pdev)
__func__, result ? "PASSED" : "FAILED");
/* We need the vector. There are no fallbacks. */
return result;
return result ? 0 : -ENXIO;
}
/*
@ -564,12 +564,14 @@ static int vmci_guest_probe_device(struct pci_dev *pdev,
dev_warn(&pdev->dev,
"VMCI device unable to register notification bitmap with PPN 0x%x\n",
(u32) bitmap_ppn);
error = -ENXIO;
goto err_remove_vmci_dev_g;
}
}
/* Check host capabilities. */
if (!vmci_check_host_caps(pdev))
error = vmci_check_host_caps(pdev);
if (error)
goto err_remove_bitmap;
/* Enable device. */

View File

@ -2600,8 +2600,6 @@ enum parport_pc_pci_cards {
syba_2p_epp,
syba_1p_ecp,
titan_010l,
titan_1284p1,
titan_1284p2,
avlab_1p,
avlab_2p,
oxsemi_952,
@ -2660,8 +2658,6 @@ static struct parport_pc_pci {
/* syba_2p_epp AP138B */ { 2, { { 0, 0x078 }, { 0, 0x178 }, } },
/* syba_1p_ecp W83787 */ { 1, { { 0, 0x078 }, } },
/* titan_010l */ { 1, { { 3, -1 }, } },
/* titan_1284p1 */ { 1, { { 0, 1 }, } },
/* titan_1284p2 */ { 2, { { 0, 1 }, { 2, 3 }, } },
/* avlab_1p */ { 1, { { 0, 1}, } },
/* avlab_2p */ { 2, { { 0, 1}, { 2, 3 },} },
/* The Oxford Semi cards are unusual: 954 doesn't support ECP,
@ -2677,8 +2673,8 @@ static struct parport_pc_pci {
/* netmos_9705 */ { 1, { { 0, -1 }, } },
/* netmos_9715 */ { 2, { { 0, 1 }, { 2, 3 },} },
/* netmos_9755 */ { 2, { { 0, 1 }, { 2, 3 },} },
/* netmos_9805 */ { 1, { { 0, -1 }, } },
/* netmos_9815 */ { 2, { { 0, -1 }, { 2, -1 }, } },
/* netmos_9805 */ { 1, { { 0, 1 }, } },
/* netmos_9815 */ { 2, { { 0, 1 }, { 2, 3 }, } },
/* netmos_9901 */ { 1, { { 0, -1 }, } },
/* netmos_9865 */ { 1, { { 0, -1 }, } },
/* quatech_sppxp100 */ { 1, { { 0, 1 }, } },
@ -2722,8 +2718,6 @@ static const struct pci_device_id parport_pc_pci_tbl[] = {
PCI_ANY_ID, PCI_ANY_ID, 0, 0, syba_1p_ecp },
{ PCI_VENDOR_ID_TITAN, PCI_DEVICE_ID_TITAN_010L,
PCI_ANY_ID, PCI_ANY_ID, 0, 0, titan_010l },
{ 0x9710, 0x9805, 0x1000, 0x0010, 0, 0, titan_1284p1 },
{ 0x9710, 0x9815, 0x1000, 0x0020, 0, 0, titan_1284p2 },
/* PCI_VENDOR_ID_AVLAB/Intek21 has another bunch of cards ...*/
/* AFAVLAB_TK9902 */
{ 0x14db, 0x2120, PCI_ANY_ID, PCI_ANY_ID, 0, 0, avlab_1p},
@ -2827,16 +2821,12 @@ static int parport_pc_pci_probe(struct pci_dev *dev,
if (irq == IRQ_NONE) {
printk(KERN_DEBUG
"PCI parallel port detected: %04x:%04x, I/O at %#lx(%#lx)\n",
parport_pc_pci_tbl[i + last_sio].vendor,
parport_pc_pci_tbl[i + last_sio].device,
io_lo, io_hi);
id->vendor, id->device, io_lo, io_hi);
irq = PARPORT_IRQ_NONE;
} else {
printk(KERN_DEBUG
"PCI parallel port detected: %04x:%04x, I/O at %#lx(%#lx), IRQ %d\n",
parport_pc_pci_tbl[i + last_sio].vendor,
parport_pc_pci_tbl[i + last_sio].device,
io_lo, io_hi, irq);
id->vendor, id->device, io_lo, io_hi, irq);
}
data->ports[count] =
parport_pc_probe_port(io_lo, io_hi, irq,
@ -2866,8 +2856,6 @@ static void parport_pc_pci_remove(struct pci_dev *dev)
struct pci_parport_data *data = pci_get_drvdata(dev);
int i;
pci_set_drvdata(dev, NULL);
if (data) {
for (i = data->num - 1; i >= 0; i--)
parport_pc_unregister_port(data->ports[i]);

View File

@ -303,7 +303,7 @@ static int bfin_cf_remove(struct platform_device *pdev)
static struct platform_driver bfin_cf_driver = {
.driver = {
.name = (char *)driver_name,
.name = driver_name,
.owner = THIS_MODULE,
},
.probe = bfin_cf_probe,

View File

@ -359,7 +359,7 @@ MODULE_DEVICE_TABLE(of, electra_cf_match);
static struct platform_driver electra_cf_driver = {
.driver = {
.name = (char *)driver_name,
.name = driver_name,
.owner = THIS_MODULE,
.of_match_table = electra_cf_match,
},

View File

@ -21,6 +21,12 @@ config PHY_EXYNOS_MIPI_VIDEO
Support for MIPI CSI-2 and MIPI DSI DPHY found on Samsung S5P
and EXYNOS SoCs.
config PHY_MVEBU_SATA
def_bool y
depends on ARCH_KIRKWOOD || ARCH_DOVE
depends on OF
select GENERIC_PHY
config OMAP_USB2
tristate "OMAP USB2 PHY Driver"
depends on ARCH_OMAP2PLUS

View File

@ -5,5 +5,6 @@
obj-$(CONFIG_GENERIC_PHY) += phy-core.o
obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o

View File

@ -94,19 +94,31 @@ static struct phy_provider *of_phy_provider_lookup(struct device_node *node)
int phy_pm_runtime_get(struct phy *phy)
{
int ret;
if (!pm_runtime_enabled(&phy->dev))
return -ENOTSUPP;
return pm_runtime_get(&phy->dev);
ret = pm_runtime_get(&phy->dev);
if (ret < 0 && ret != -EINPROGRESS)
pm_runtime_put_noidle(&phy->dev);
return ret;
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_get);
int phy_pm_runtime_get_sync(struct phy *phy)
{
int ret;
if (!pm_runtime_enabled(&phy->dev))
return -ENOTSUPP;
return pm_runtime_get_sync(&phy->dev);
ret = pm_runtime_get_sync(&phy->dev);
if (ret < 0)
pm_runtime_put_sync(&phy->dev);
return ret;
}
EXPORT_SYMBOL_GPL(phy_pm_runtime_get_sync);
@ -155,13 +167,14 @@ int phy_init(struct phy *phy)
return ret;
mutex_lock(&phy->mutex);
if (phy->init_count++ == 0 && phy->ops->init) {
if (phy->init_count == 0 && phy->ops->init) {
ret = phy->ops->init(phy);
if (ret < 0) {
dev_err(&phy->dev, "phy init failed --> %d\n", ret);
goto out;
}
}
++phy->init_count;
out:
mutex_unlock(&phy->mutex);
@ -179,13 +192,14 @@ int phy_exit(struct phy *phy)
return ret;
mutex_lock(&phy->mutex);
if (--phy->init_count == 0 && phy->ops->exit) {
if (phy->init_count == 1 && phy->ops->exit) {
ret = phy->ops->exit(phy);
if (ret < 0) {
dev_err(&phy->dev, "phy exit failed --> %d\n", ret);
goto out;
}
}
--phy->init_count;
out:
mutex_unlock(&phy->mutex);
@ -196,23 +210,27 @@ EXPORT_SYMBOL_GPL(phy_exit);
int phy_power_on(struct phy *phy)
{
int ret = -ENOTSUPP;
int ret;
ret = phy_pm_runtime_get_sync(phy);
if (ret < 0 && ret != -ENOTSUPP)
return ret;
mutex_lock(&phy->mutex);
if (phy->power_count++ == 0 && phy->ops->power_on) {
if (phy->power_count == 0 && phy->ops->power_on) {
ret = phy->ops->power_on(phy);
if (ret < 0) {
dev_err(&phy->dev, "phy poweron failed --> %d\n", ret);
goto out;
}
}
++phy->power_count;
mutex_unlock(&phy->mutex);
return 0;
out:
mutex_unlock(&phy->mutex);
phy_pm_runtime_put_sync(phy);
return ret;
}
@ -220,22 +238,22 @@ EXPORT_SYMBOL_GPL(phy_power_on);
int phy_power_off(struct phy *phy)
{
int ret = -ENOTSUPP;
int ret;
mutex_lock(&phy->mutex);
if (--phy->power_count == 0 && phy->ops->power_off) {
if (phy->power_count == 1 && phy->ops->power_off) {
ret = phy->ops->power_off(phy);
if (ret < 0) {
dev_err(&phy->dev, "phy poweroff failed --> %d\n", ret);
goto out;
mutex_unlock(&phy->mutex);
return ret;
}
}
out:
--phy->power_count;
mutex_unlock(&phy->mutex);
phy_pm_runtime_put(phy);
return ret;
return 0;
}
EXPORT_SYMBOL_GPL(phy_power_off);
@ -360,7 +378,7 @@ EXPORT_SYMBOL_GPL(of_phy_simple_xlate);
struct phy *phy_get(struct device *dev, const char *string)
{
int index = 0;
struct phy *phy = NULL;
struct phy *phy;
if (string == NULL) {
dev_WARN(dev, "missing string\n");

View File

@ -0,0 +1,137 @@
/*
* phy-mvebu-sata.c: SATA Phy driver for the Marvell mvebu SoCs.
*
* Copyright (C) 2013 Andrew Lunn <andrew@lunn.ch>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version
* 2 of the License, or (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/phy/phy.h>
#include <linux/io.h>
#include <linux/platform_device.h>
struct priv {
struct clk *clk;
void __iomem *base;
};
#define SATA_PHY_MODE_2 0x0330
#define MODE_2_FORCE_PU_TX BIT(0)
#define MODE_2_FORCE_PU_RX BIT(1)
#define MODE_2_PU_PLL BIT(2)
#define MODE_2_PU_IVREF BIT(3)
#define SATA_IF_CTRL 0x0050
#define CTRL_PHY_SHUTDOWN BIT(9)
static int phy_mvebu_sata_power_on(struct phy *phy)
{
struct priv *priv = phy_get_drvdata(phy);
u32 reg;
clk_prepare_enable(priv->clk);
/* Enable PLL and IVREF */
reg = readl(priv->base + SATA_PHY_MODE_2);
reg |= (MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX |
MODE_2_PU_PLL | MODE_2_PU_IVREF);
writel(reg , priv->base + SATA_PHY_MODE_2);
/* Enable PHY */
reg = readl(priv->base + SATA_IF_CTRL);
reg &= ~CTRL_PHY_SHUTDOWN;
writel(reg, priv->base + SATA_IF_CTRL);
clk_disable_unprepare(priv->clk);
return 0;
}
static int phy_mvebu_sata_power_off(struct phy *phy)
{
struct priv *priv = phy_get_drvdata(phy);
u32 reg;
clk_prepare_enable(priv->clk);
/* Disable PLL and IVREF */
reg = readl(priv->base + SATA_PHY_MODE_2);
reg &= ~(MODE_2_FORCE_PU_TX | MODE_2_FORCE_PU_RX |
MODE_2_PU_PLL | MODE_2_PU_IVREF);
writel(reg, priv->base + SATA_PHY_MODE_2);
/* Disable PHY */
reg = readl(priv->base + SATA_IF_CTRL);
reg |= CTRL_PHY_SHUTDOWN;
writel(reg, priv->base + SATA_IF_CTRL);
clk_disable_unprepare(priv->clk);
return 0;
}
static struct phy_ops phy_mvebu_sata_ops = {
.power_on = phy_mvebu_sata_power_on,
.power_off = phy_mvebu_sata_power_off,
.owner = THIS_MODULE,
};
static int phy_mvebu_sata_probe(struct platform_device *pdev)
{
struct phy_provider *phy_provider;
struct resource *res;
struct priv *priv;
struct phy *phy;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
priv->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(priv->base))
return PTR_ERR(priv->base);
priv->clk = devm_clk_get(&pdev->dev, "sata");
if (IS_ERR(priv->clk))
return PTR_ERR(priv->clk);
phy_provider = devm_of_phy_provider_register(&pdev->dev,
of_phy_simple_xlate);
if (IS_ERR(phy_provider))
return PTR_ERR(phy_provider);
phy = devm_phy_create(&pdev->dev, &phy_mvebu_sata_ops, NULL);
if (IS_ERR(phy))
return PTR_ERR(phy);
phy_set_drvdata(phy, priv);
/* The boot loader may of left it on. Turn it off. */
phy_mvebu_sata_power_off(phy);
return 0;
}
static const struct of_device_id phy_mvebu_sata_of_match[] = {
{ .compatible = "marvell,mvebu-sata-phy" },
{ },
};
MODULE_DEVICE_TABLE(of, phy_mvebu_sata_of_match);
static struct platform_driver phy_mvebu_sata_driver = {
.probe = phy_mvebu_sata_probe,
.driver = {
.name = "phy-mvebu-sata",
.owner = THIS_MODULE,
.of_match_table = phy_mvebu_sata_of_match,
}
};
module_platform_driver(phy_mvebu_sata_driver);
MODULE_AUTHOR("Andrew Lunn <andrew@lunn.ch>");
MODULE_DESCRIPTION("Marvell MVEBU SATA PHY driver");
MODULE_LICENSE("GPL v2");

View File

@ -847,7 +847,7 @@ int __uio_register_device(struct module *owner,
info->uio_dev = idev;
if (info->irq && (info->irq != UIO_IRQ_CUSTOM)) {
ret = devm_request_irq(parent, info->irq, uio_interrupt,
ret = devm_request_irq(idev->dev, info->irq, uio_interrupt,
info->irq_flags, info->name, idev);
if (ret)
goto err_request_irq;

View File

@ -228,7 +228,7 @@ static void mf624_pci_remove(struct pci_dev *dev)
kfree(info);
}
static DEFINE_PCI_DEVICE_TABLE(mf624_pci_id) = {
static const struct pci_device_id mf624_pci_id[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_HUMUSOFT, PCI_DEVICE_ID_MF624) },
{ 0, }
};

View File

@ -46,7 +46,6 @@
struct mxc_w1_device {
void __iomem *regs;
unsigned int clkdiv;
struct clk *clk;
struct w1_bus_master bus_master;
};
@ -106,8 +105,10 @@ static u8 mxc_w1_ds2_touch_bit(void *data, u8 bit)
static int mxc_w1_probe(struct platform_device *pdev)
{
struct mxc_w1_device *mdev;
unsigned long clkrate;
struct resource *res;
int err = 0;
unsigned int clkdiv;
int err;
mdev = devm_kzalloc(&pdev->dev, sizeof(struct mxc_w1_device),
GFP_KERNEL);
@ -118,27 +119,39 @@ static int mxc_w1_probe(struct platform_device *pdev)
if (IS_ERR(mdev->clk))
return PTR_ERR(mdev->clk);
mdev->clkdiv = (clk_get_rate(mdev->clk) / 1000000) - 1;
clkrate = clk_get_rate(mdev->clk);
if (clkrate < 10000000)
dev_warn(&pdev->dev,
"Low clock frequency causes improper function\n");
clkdiv = DIV_ROUND_CLOSEST(clkrate, 1000000);
clkrate /= clkdiv;
if ((clkrate < 980000) || (clkrate > 1020000))
dev_warn(&pdev->dev,
"Incorrect time base frequency %lu Hz\n", clkrate);
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
mdev->regs = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mdev->regs))
return PTR_ERR(mdev->regs);
clk_prepare_enable(mdev->clk);
__raw_writeb(mdev->clkdiv, mdev->regs + MXC_W1_TIME_DIVIDER);
err = clk_prepare_enable(mdev->clk);
if (err)
return err;
__raw_writeb(clkdiv - 1, mdev->regs + MXC_W1_TIME_DIVIDER);
mdev->bus_master.data = mdev;
mdev->bus_master.reset_bus = mxc_w1_ds2_reset_bus;
mdev->bus_master.touch_bit = mxc_w1_ds2_touch_bit;
err = w1_add_master_device(&mdev->bus_master);
if (err)
return err;
platform_set_drvdata(pdev, mdev);
return 0;
err = w1_add_master_device(&mdev->bus_master);
if (err)
clk_disable_unprepare(mdev->clk);
return err;
}
/*

View File

@ -51,6 +51,7 @@ struct gpio_extcon_platform_data {
/* if NULL, "0" or "1" will be printed */
const char *state_on;
const char *state_off;
bool check_on_resume;
};
#endif /* __EXTCON_GPIO_H__ */

View File

@ -2196,6 +2196,15 @@
/*
* R677 (0x2A5) - Mic Detect 3
*/
#define ARIZONA_MICD_LVL_0 0x0004 /* MICD_LVL - [2] */
#define ARIZONA_MICD_LVL_1 0x0008 /* MICD_LVL - [3] */
#define ARIZONA_MICD_LVL_2 0x0010 /* MICD_LVL - [4] */
#define ARIZONA_MICD_LVL_3 0x0020 /* MICD_LVL - [5] */
#define ARIZONA_MICD_LVL_4 0x0040 /* MICD_LVL - [6] */
#define ARIZONA_MICD_LVL_5 0x0080 /* MICD_LVL - [7] */
#define ARIZONA_MICD_LVL_6 0x0100 /* MICD_LVL - [8] */
#define ARIZONA_MICD_LVL_7 0x0200 /* MICD_LVL - [9] */
#define ARIZONA_MICD_LVL_8 0x0400 /* MICD_LVL - [10] */
#define ARIZONA_MICD_LVL_MASK 0x07FC /* MICD_LVL - [10:2] */
#define ARIZONA_MICD_LVL_SHIFT 2 /* MICD_LVL - [10:2] */
#define ARIZONA_MICD_LVL_WIDTH 9 /* MICD_LVL - [10:2] */

View File

@ -0,0 +1,500 @@
#ifndef __GENWQE_CARD_H__
#define __GENWQE_CARD_H__
/**
* IBM Accelerator Family 'GenWQE'
*
* (C) Copyright IBM Corp. 2013
*
* Author: Frank Haverkamp <haver@linux.vnet.ibm.com>
* Author: Joerg-Stephan Vogt <jsvogt@de.ibm.com>
* Author: Michael Jung <mijung@de.ibm.com>
* Author: Michael Ruettger <michael@ibmra.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License (version 2 only)
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
/*
* User-space API for the GenWQE card. For debugging and test purposes
* the register addresses are included here too.
*/
#include <linux/types.h>
#include <linux/ioctl.h>
/* Basename of sysfs, debugfs and /dev interfaces */
#define GENWQE_DEVNAME "genwqe"
#define GENWQE_TYPE_ALTERA_230 0x00 /* GenWQE4 Stratix-IV-230 */
#define GENWQE_TYPE_ALTERA_530 0x01 /* GenWQE4 Stratix-IV-530 */
#define GENWQE_TYPE_ALTERA_A4 0x02 /* GenWQE5 A4 Stratix-V-A4 */
#define GENWQE_TYPE_ALTERA_A7 0x03 /* GenWQE5 A7 Stratix-V-A7 */
/* MMIO Unit offsets: Each UnitID occupies a defined address range */
#define GENWQE_UID_OFFS(uid) ((uid) << 24)
#define GENWQE_SLU_OFFS GENWQE_UID_OFFS(0)
#define GENWQE_HSU_OFFS GENWQE_UID_OFFS(1)
#define GENWQE_APP_OFFS GENWQE_UID_OFFS(2)
#define GENWQE_MAX_UNITS 3
/* Common offsets per UnitID */
#define IO_EXTENDED_ERROR_POINTER 0x00000048
#define IO_ERROR_INJECT_SELECTOR 0x00000060
#define IO_EXTENDED_DIAG_SELECTOR 0x00000070
#define IO_EXTENDED_DIAG_READ_MBX 0x00000078
#define IO_EXTENDED_DIAG_MAP(ring) (0x00000500 | ((ring) << 3))
#define GENWQE_EXTENDED_DIAG_SELECTOR(ring, trace) (((ring) << 8) | (trace))
/* UnitID 0: Service Layer Unit (SLU) */
/* SLU: Unit Configuration Register */
#define IO_SLU_UNITCFG 0x00000000
#define IO_SLU_UNITCFG_TYPE_MASK 0x000000000ff00000 /* 27:20 */
/* SLU: Fault Isolation Register (FIR) (ac_slu_fir) */
#define IO_SLU_FIR 0x00000008 /* read only, wr direct */
#define IO_SLU_FIR_CLR 0x00000010 /* read and clear */
/* SLU: First Error Capture Register (FEC/WOF) */
#define IO_SLU_FEC 0x00000018
#define IO_SLU_ERR_ACT_MASK 0x00000020
#define IO_SLU_ERR_ATTN_MASK 0x00000028
#define IO_SLU_FIRX1_ACT_MASK 0x00000030
#define IO_SLU_FIRX0_ACT_MASK 0x00000038
#define IO_SLU_SEC_LEM_DEBUG_OVR 0x00000040
#define IO_SLU_EXTENDED_ERR_PTR 0x00000048
#define IO_SLU_COMMON_CONFIG 0x00000060
#define IO_SLU_FLASH_FIR 0x00000108
#define IO_SLU_SLC_FIR 0x00000110
#define IO_SLU_RIU_TRAP 0x00000280
#define IO_SLU_FLASH_FEC 0x00000308
#define IO_SLU_SLC_FEC 0x00000310
/*
* The Virtual Function's Access is from offset 0x00010000
* The Physical Function's Access is from offset 0x00050000
* Single Shared Registers exists only at offset 0x00060000
*
* SLC: Queue Virtual Window Window for accessing into a specific VF
* queue. When accessing the 0x10000 space using the 0x50000 address
* segment, the value indicated here is used to specify which VF
* register is decoded. This register, and the 0x50000 register space
* can only be accessed by the PF. Example, if this register is set to
* 0x2, then a read from 0x50000 is the same as a read from 0x10000
* from VF=2.
*/
/* SLC: Queue Segment */
#define IO_SLC_QUEUE_SEGMENT 0x00010000
#define IO_SLC_VF_QUEUE_SEGMENT 0x00050000
/* SLC: Queue Offset */
#define IO_SLC_QUEUE_OFFSET 0x00010008
#define IO_SLC_VF_QUEUE_OFFSET 0x00050008
/* SLC: Queue Configuration */
#define IO_SLC_QUEUE_CONFIG 0x00010010
#define IO_SLC_VF_QUEUE_CONFIG 0x00050010
/* SLC: Job Timout/Only accessible for the PF */
#define IO_SLC_APPJOB_TIMEOUT 0x00010018
#define IO_SLC_VF_APPJOB_TIMEOUT 0x00050018
#define TIMEOUT_250MS 0x0000000f
#define HEARTBEAT_DISABLE 0x0000ff00
/* SLC: Queue InitSequence Register */
#define IO_SLC_QUEUE_INITSQN 0x00010020
#define IO_SLC_VF_QUEUE_INITSQN 0x00050020
/* SLC: Queue Wrap */
#define IO_SLC_QUEUE_WRAP 0x00010028
#define IO_SLC_VF_QUEUE_WRAP 0x00050028
/* SLC: Queue Status */
#define IO_SLC_QUEUE_STATUS 0x00010100
#define IO_SLC_VF_QUEUE_STATUS 0x00050100
/* SLC: Queue Working Time */
#define IO_SLC_QUEUE_WTIME 0x00010030
#define IO_SLC_VF_QUEUE_WTIME 0x00050030
/* SLC: Queue Error Counts */
#define IO_SLC_QUEUE_ERRCNTS 0x00010038
#define IO_SLC_VF_QUEUE_ERRCNTS 0x00050038
/* SLC: Queue Loast Response Word */
#define IO_SLC_QUEUE_LRW 0x00010040
#define IO_SLC_VF_QUEUE_LRW 0x00050040
/* SLC: Freerunning Timer */
#define IO_SLC_FREE_RUNNING_TIMER 0x00010108
#define IO_SLC_VF_FREE_RUNNING_TIMER 0x00050108
/* SLC: Queue Virtual Access Region */
#define IO_PF_SLC_VIRTUAL_REGION 0x00050000
/* SLC: Queue Virtual Window */
#define IO_PF_SLC_VIRTUAL_WINDOW 0x00060000
/* SLC: DDCB Application Job Pending [n] (n=0:63) */
#define IO_PF_SLC_JOBPEND(n) (0x00061000 + 8*(n))
#define IO_SLC_JOBPEND(n) IO_PF_SLC_JOBPEND(n)
/* SLC: Parser Trap RAM [n] (n=0:31) */
#define IO_SLU_SLC_PARSE_TRAP(n) (0x00011000 + 8*(n))
/* SLC: Dispatcher Trap RAM [n] (n=0:31) */
#define IO_SLU_SLC_DISP_TRAP(n) (0x00011200 + 8*(n))
/* Global Fault Isolation Register (GFIR) */
#define IO_SLC_CFGREG_GFIR 0x00020000
#define GFIR_ERR_TRIGGER 0x0000ffff
/* SLU: Soft Reset Register */
#define IO_SLC_CFGREG_SOFTRESET 0x00020018
/* SLU: Misc Debug Register */
#define IO_SLC_MISC_DEBUG 0x00020060
#define IO_SLC_MISC_DEBUG_CLR 0x00020068
#define IO_SLC_MISC_DEBUG_SET 0x00020070
/* Temperature Sensor Reading */
#define IO_SLU_TEMPERATURE_SENSOR 0x00030000
#define IO_SLU_TEMPERATURE_CONFIG 0x00030008
/* Voltage Margining Control */
#define IO_SLU_VOLTAGE_CONTROL 0x00030080
#define IO_SLU_VOLTAGE_NOMINAL 0x00000000
#define IO_SLU_VOLTAGE_DOWN5 0x00000006
#define IO_SLU_VOLTAGE_UP5 0x00000007
/* Direct LED Control Register */
#define IO_SLU_LEDCONTROL 0x00030100
/* SLU: Flashbus Direct Access -A5 */
#define IO_SLU_FLASH_DIRECTACCESS 0x00040010
/* SLU: Flashbus Direct Access2 -A5 */
#define IO_SLU_FLASH_DIRECTACCESS2 0x00040020
/* SLU: Flashbus Command Interface -A5 */
#define IO_SLU_FLASH_CMDINTF 0x00040030
/* SLU: BitStream Loaded */
#define IO_SLU_BITSTREAM 0x00040040
/* This Register has a switch which will change the CAs to UR */
#define IO_HSU_ERR_BEHAVIOR 0x01001010
#define IO_SLC2_SQB_TRAP 0x00062000
#define IO_SLC2_QUEUE_MANAGER_TRAP 0x00062008
#define IO_SLC2_FLS_MASTER_TRAP 0x00062010
/* UnitID 1: HSU Registers */
#define IO_HSU_UNITCFG 0x01000000
#define IO_HSU_FIR 0x01000008
#define IO_HSU_FIR_CLR 0x01000010
#define IO_HSU_FEC 0x01000018
#define IO_HSU_ERR_ACT_MASK 0x01000020
#define IO_HSU_ERR_ATTN_MASK 0x01000028
#define IO_HSU_FIRX1_ACT_MASK 0x01000030
#define IO_HSU_FIRX0_ACT_MASK 0x01000038
#define IO_HSU_SEC_LEM_DEBUG_OVR 0x01000040
#define IO_HSU_EXTENDED_ERR_PTR 0x01000048
#define IO_HSU_COMMON_CONFIG 0x01000060
/* UnitID 2: Application Unit (APP) */
#define IO_APP_UNITCFG 0x02000000
#define IO_APP_FIR 0x02000008
#define IO_APP_FIR_CLR 0x02000010
#define IO_APP_FEC 0x02000018
#define IO_APP_ERR_ACT_MASK 0x02000020
#define IO_APP_ERR_ATTN_MASK 0x02000028
#define IO_APP_FIRX1_ACT_MASK 0x02000030
#define IO_APP_FIRX0_ACT_MASK 0x02000038
#define IO_APP_SEC_LEM_DEBUG_OVR 0x02000040
#define IO_APP_EXTENDED_ERR_PTR 0x02000048
#define IO_APP_COMMON_CONFIG 0x02000060
#define IO_APP_DEBUG_REG_01 0x02010000
#define IO_APP_DEBUG_REG_02 0x02010008
#define IO_APP_DEBUG_REG_03 0x02010010
#define IO_APP_DEBUG_REG_04 0x02010018
#define IO_APP_DEBUG_REG_05 0x02010020
#define IO_APP_DEBUG_REG_06 0x02010028
#define IO_APP_DEBUG_REG_07 0x02010030
#define IO_APP_DEBUG_REG_08 0x02010038
#define IO_APP_DEBUG_REG_09 0x02010040
#define IO_APP_DEBUG_REG_10 0x02010048
#define IO_APP_DEBUG_REG_11 0x02010050
#define IO_APP_DEBUG_REG_12 0x02010058
#define IO_APP_DEBUG_REG_13 0x02010060
#define IO_APP_DEBUG_REG_14 0x02010068
#define IO_APP_DEBUG_REG_15 0x02010070
#define IO_APP_DEBUG_REG_16 0x02010078
#define IO_APP_DEBUG_REG_17 0x02010080
#define IO_APP_DEBUG_REG_18 0x02010088
/* Read/write from/to registers */
struct genwqe_reg_io {
__u64 num; /* register offset/address */
__u64 val64;
};
/*
* All registers of our card will return values not equal this values.
* If we see IO_ILLEGAL_VALUE on any of our MMIO register reads, the
* card can be considered as unusable. It will need recovery.
*/
#define IO_ILLEGAL_VALUE 0xffffffffffffffffull
/*
* Generic DDCB execution interface.
*
* This interface is a first prototype resulting from discussions we
* had with other teams which wanted to use the Genwqe card. It allows
* to issue a DDCB request in a generic way. The request will block
* until it finishes or time out with error.
*
* Some DDCBs require DMA addresses to be specified in the ASIV
* block. The interface provies the capability to let the kernel
* driver know where those addresses are by specifying the ATS field,
* such that it can replace the user-space addresses with appropriate
* DMA addresses or DMA addresses of a scatter gather list which is
* dynamically created.
*
* Our hardware will refuse DDCB execution if the ATS field is not as
* expected. That means the DDCB execution engine in the chip knows
* where it expects DMA addresses within the ASIV part of the DDCB and
* will check that against the ATS field definition. Any invalid or
* unknown ATS content will lead to DDCB refusal.
*/
/* Genwqe chip Units */
#define DDCB_ACFUNC_SLU 0x00 /* chip service layer unit */
#define DDCB_ACFUNC_APP 0x01 /* chip application */
/* DDCB return codes (RETC) */
#define DDCB_RETC_IDLE 0x0000 /* Unexecuted/DDCB created */
#define DDCB_RETC_PENDING 0x0101 /* Pending Execution */
#define DDCB_RETC_COMPLETE 0x0102 /* Cmd complete. No error */
#define DDCB_RETC_FAULT 0x0104 /* App Err, recoverable */
#define DDCB_RETC_ERROR 0x0108 /* App Err, non-recoverable */
#define DDCB_RETC_FORCED_ERROR 0x01ff /* overwritten by driver */
#define DDCB_RETC_UNEXEC 0x0110 /* Unexe/Removed from queue */
#define DDCB_RETC_TERM 0x0120 /* Terminated */
#define DDCB_RETC_RES0 0x0140 /* Reserved */
#define DDCB_RETC_RES1 0x0180 /* Reserved */
/* DDCB Command Options (CMDOPT) */
#define DDCB_OPT_ECHO_FORCE_NO 0x0000 /* ECHO DDCB */
#define DDCB_OPT_ECHO_FORCE_102 0x0001 /* force return code */
#define DDCB_OPT_ECHO_FORCE_104 0x0002
#define DDCB_OPT_ECHO_FORCE_108 0x0003
#define DDCB_OPT_ECHO_FORCE_110 0x0004 /* only on PF ! */
#define DDCB_OPT_ECHO_FORCE_120 0x0005
#define DDCB_OPT_ECHO_FORCE_140 0x0006
#define DDCB_OPT_ECHO_FORCE_180 0x0007
#define DDCB_OPT_ECHO_COPY_NONE (0 << 5)
#define DDCB_OPT_ECHO_COPY_ALL (1 << 5)
/* Definitions of Service Layer Commands */
#define SLCMD_ECHO_SYNC 0x00 /* PF/VF */
#define SLCMD_MOVE_FLASH 0x06 /* PF only */
#define SLCMD_MOVE_FLASH_FLAGS_MODE 0x03 /* bit 0 and 1 used for mode */
#define SLCMD_MOVE_FLASH_FLAGS_DLOAD 0 /* mode: download */
#define SLCMD_MOVE_FLASH_FLAGS_EMUL 1 /* mode: emulation */
#define SLCMD_MOVE_FLASH_FLAGS_UPLOAD 2 /* mode: upload */
#define SLCMD_MOVE_FLASH_FLAGS_VERIFY 3 /* mode: verify */
#define SLCMD_MOVE_FLASH_FLAG_NOTAP (1 << 2)/* just dump DDCB and exit */
#define SLCMD_MOVE_FLASH_FLAG_POLL (1 << 3)/* wait for RETC >= 0102 */
#define SLCMD_MOVE_FLASH_FLAG_PARTITION (1 << 4)
#define SLCMD_MOVE_FLASH_FLAG_ERASE (1 << 5)
enum genwqe_card_state {
GENWQE_CARD_UNUSED = 0,
GENWQE_CARD_USED = 1,
GENWQE_CARD_FATAL_ERROR = 2,
GENWQE_CARD_STATE_MAX,
};
/* common struct for chip image exchange */
struct genwqe_bitstream {
__u64 data_addr; /* pointer to image data */
__u32 size; /* size of image file */
__u32 crc; /* crc of this image */
__u64 target_addr; /* starting address in Flash */
__u32 partition; /* '0', '1', or 'v' */
__u32 uid; /* 1=host/x=dram */
__u64 slu_id; /* informational/sim: SluID */
__u64 app_id; /* informational/sim: AppID */
__u16 retc; /* returned from processing */
__u16 attn; /* attention code from processing */
__u32 progress; /* progress code from processing */
};
/* Issuing a specific DDCB command */
#define DDCB_LENGTH 256 /* for debug data */
#define DDCB_ASIV_LENGTH 104 /* len of the DDCB ASIV array */
#define DDCB_ASIV_LENGTH_ATS 96 /* ASIV in ATS architecture */
#define DDCB_ASV_LENGTH 64 /* len of the DDCB ASV array */
#define DDCB_FIXUPS 12 /* maximum number of fixups */
struct genwqe_debug_data {
char driver_version[64];
__u64 slu_unitcfg;
__u64 app_unitcfg;
__u8 ddcb_before[DDCB_LENGTH];
__u8 ddcb_prev[DDCB_LENGTH];
__u8 ddcb_finished[DDCB_LENGTH];
};
/*
* Address Translation Specification (ATS) definitions
*
* Each 4 bit within the ATS 64-bit word specify the required address
* translation at the defined offset.
*
* 63 LSB
* 6666.5555.5555.5544.4444.4443.3333.3333 ... 11
* 3210.9876.5432.1098.7654.3210.9876.5432 ... 1098.7654.3210
*
* offset: 0x00 0x08 0x10 0x18 0x20 0x28 0x30 0x38 ... 0x68 0x70 0x78
* res res res res ASIV ...
* The first 4 entries in the ATS word are reserved. The following nibbles
* each describe at an 8 byte offset the format of the required data.
*/
#define ATS_TYPE_DATA 0x0ull /* data */
#define ATS_TYPE_FLAT_RD 0x4ull /* flat buffer read only */
#define ATS_TYPE_FLAT_RDWR 0x5ull /* flat buffer read/write */
#define ATS_TYPE_SGL_RD 0x6ull /* sgl read only */
#define ATS_TYPE_SGL_RDWR 0x7ull /* sgl read/write */
#define ATS_SET_FLAGS(_struct, _field, _flags) \
(((_flags) & 0xf) << (44 - (4 * (offsetof(_struct, _field) / 8))))
#define ATS_GET_FLAGS(_ats, _byte_offs) \
(((_ats) >> (44 - (4 * ((_byte_offs) / 8)))) & 0xf)
/**
* struct genwqe_ddcb_cmd - User parameter for generic DDCB commands
*
* On the way into the kernel the driver will read the whole data
* structure. On the way out the driver will not copy the ASIV data
* back to user-space.
*/
struct genwqe_ddcb_cmd {
/* START of data copied to/from driver */
__u64 next_addr; /* chaining genwqe_ddcb_cmd */
__u64 flags; /* reserved */
__u8 acfunc; /* accelerators functional unit */
__u8 cmd; /* command to execute */
__u8 asiv_length; /* used parameter length */
__u8 asv_length; /* length of valid return values */
__u16 cmdopts; /* command options */
__u16 retc; /* return code from processing */
__u16 attn; /* attention code from processing */
__u16 vcrc; /* variant crc16 */
__u32 progress; /* progress code from processing */
__u64 deque_ts; /* dequeue time stamp */
__u64 cmplt_ts; /* completion time stamp */
__u64 disp_ts; /* SW processing start */
/* move to end and avoid copy-back */
__u64 ddata_addr; /* collect debug data */
/* command specific values */
__u8 asv[DDCB_ASV_LENGTH];
/* END of data copied from driver */
union {
struct {
__u64 ats;
__u8 asiv[DDCB_ASIV_LENGTH_ATS];
};
/* used for flash update to keep it backward compatible */
__u8 __asiv[DDCB_ASIV_LENGTH];
};
/* END of data copied to driver */
};
#define GENWQE_IOC_CODE 0xa5
/* Access functions */
#define GENWQE_READ_REG64 _IOR(GENWQE_IOC_CODE, 30, struct genwqe_reg_io)
#define GENWQE_WRITE_REG64 _IOW(GENWQE_IOC_CODE, 31, struct genwqe_reg_io)
#define GENWQE_READ_REG32 _IOR(GENWQE_IOC_CODE, 32, struct genwqe_reg_io)
#define GENWQE_WRITE_REG32 _IOW(GENWQE_IOC_CODE, 33, struct genwqe_reg_io)
#define GENWQE_READ_REG16 _IOR(GENWQE_IOC_CODE, 34, struct genwqe_reg_io)
#define GENWQE_WRITE_REG16 _IOW(GENWQE_IOC_CODE, 35, struct genwqe_reg_io)
#define GENWQE_GET_CARD_STATE _IOR(GENWQE_IOC_CODE, 36, enum genwqe_card_state)
/**
* struct genwqe_mem - Memory pinning/unpinning information
* @addr: virtual user space address
* @size: size of the area pin/dma-map/unmap
* direction: 0: read/1: read and write
*
* Avoid pinning and unpinning of memory pages dynamically. Instead
* the idea is to pin the whole buffer space required for DDCB
* opertionas in advance. The driver will reuse this pinning and the
* memory associated with it to setup the sglists for the DDCB
* requests without the need to allocate and free memory or map and
* unmap to get the DMA addresses.
*
* The inverse operation needs to be called after the pinning is not
* needed anymore. The pinnings else the pinnings will get removed
* after the device is closed. Note that pinnings will required
* memory.
*/
struct genwqe_mem {
__u64 addr;
__u64 size;
__u64 direction;
__u64 flags;
};
#define GENWQE_PIN_MEM _IOWR(GENWQE_IOC_CODE, 40, struct genwqe_mem)
#define GENWQE_UNPIN_MEM _IOWR(GENWQE_IOC_CODE, 41, struct genwqe_mem)
/*
* Generic synchronous DDCB execution interface.
* Synchronously execute a DDCB.
*
* Return: 0 on success or negative error code.
* -EINVAL: Invalid parameters (ASIV_LEN, ASV_LEN, illegal fixups
* no mappings found/could not create mappings
* -EFAULT: illegal addresses in fixups, purging failed
* -EBADMSG: enqueing failed, retc != DDCB_RETC_COMPLETE
*/
#define GENWQE_EXECUTE_DDCB \
_IOWR(GENWQE_IOC_CODE, 50, struct genwqe_ddcb_cmd)
#define GENWQE_EXECUTE_RAW_DDCB \
_IOWR(GENWQE_IOC_CODE, 51, struct genwqe_ddcb_cmd)
/* Service Layer functions (PF only) */
#define GENWQE_SLU_UPDATE _IOWR(GENWQE_IOC_CODE, 80, struct genwqe_bitstream)
#define GENWQE_SLU_READ _IOWR(GENWQE_IOC_CODE, 81, struct genwqe_bitstream)
#endif /* __GENWQE_CARD_H__ */

View File

@ -26,7 +26,6 @@
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/utsname.h>
#include <linux/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

View File

@ -22,7 +22,6 @@
#include <sys/socket.h>
#include <sys/poll.h>
#include <sys/ioctl.h>
#include <linux/types.h>
#include <fcntl.h>
#include <stdio.h>
#include <mntent.h>