提交 9f67627a 编写于 作者: L Linus Torvalds

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
  ...
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.
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.
......@@ -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
......
......@@ -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:
......
......@@ -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
......
......@@ -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:
......
......@@ -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>;
};
......@@ -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
......
......@@ -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
これらのどれもが、時にはとても困難です。これらの慣例を完璧に実施するに
は数年かかるかもしれません。これは継続的な改善のプロセスであり、そのた
......
......@@ -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
- ACPI development tree, Len Brown <len.brown@intel.com >
git.kernel.org:/pub/scm/linux/kernel/git/lenb/linux-acpi-2.6.git
- Block development tree, Jens Axboe <jens.axboe@oracle.com>
git.kernel.org:/pub/scm/linux/kernel/git/axboe/linux-2.6-block.git
- 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 파일에서 참조할 수
있다.
다양한 커널 서브시스템의 메인테이너들 --- 그리고 많은 커널 서브시스템 개발자들
--- 은 그들의 현재 개발 상태를 소스 저장소로 노출한다. 이를 통해 다른 사람들도
커널의 다른 영역에 어떤 변화가 이루어지고 있는지 알 수 있다. 급속히 개발이
진행되는 영역이 있고 그렇지 않은 영역이 있으므로, 개발자는 다른 개발자가 제출한
수정 사항과 자신의 수정사항의 충돌이나 동일한 일을 동시에 두사람이 따로
진행하는 사태를 방지하기 위해 급속히 개발이 진행되고 있는 영역에 작업의
베이스를 맞춰줄 것이 요구된다.
대부분의 이러한 저장소는 git 트리지만, git이 아닌 SCM으로 관리되거나, quilt
시리즈로 제공되는 패치들도 존재한다. 이러한 서브시스템 저장소들은 MAINTAINERS
파일에 나열되어 있다. 대부분은 http://git.kernel.org 에서 볼 수 있다.
제안된 패치는 서브시스템 트리에 커밋되기 전에 메일링 리스트를 통해
리뷰된다(아래의 관련 섹션을 참고하기 바란다). 일부 커널 서브시스템의 경우, 이
리뷰 프로세스는 patchwork라는 도구를 통해 추적된다. patchwork은 등록된 패치와
패치에 대한 코멘트, 패치의 버전을 볼 수 있는 웹 인터페이스를 제공하고,
메인테이너는 패치를 리뷰 중, 리뷰 통과, 또는 반려됨으로 표시할 수 있다.
대부분의 이러한 patchwork 사이트는 http://patchwork.kernel.org/ 또는
http://patchwork.ozlabs.org/ 에 나열되어 있다.
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/
이런 식으로, -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
......
......@@ -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);
......
......@@ -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
这些事情有时候做起来很难。要在任何方面都做到完美可能需要好几年时间。这是
......
......@@ -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/
......
......@@ -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) },
{ }
};
......
/*
* 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 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 void i8k_hwmon_remove_files(struct device *dev)
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);
}
......
......@@ -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:
......
......@@ -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;
}
......
......@@ -216,4 +216,4 @@ static int __init ttyprintk_init(void)
ttyprintk_driver = NULL;
return ret;
}
module_init(ttyprintk_init);
device_initcall(ttyprintk_init);
......@@ -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
......
......@@ -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
......
......@@ -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;
}
......
......@@ -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,
},
};
......
此差异已折叠。
......@@ -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 */ }
};
......
......@@ -301,7 +301,7 @@ int hv_synic_alloc(void)
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])
......
......@@ -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
......@@ -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/
......@@ -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,
......
......@@ -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)
......
......@@ -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)
......
......@@ -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);
......
......@@ -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);
......
......@@ -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;
}
......
#
# 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.
#
# 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
此差异已折叠。
#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__ */
此差异已折叠。
#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__ */
/**
* 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);
}
此差异已折叠。
此差异已折叠。
此差异已折叠。
#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__ */
......@@ -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);
......
......@@ -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;
......
......@@ -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 @@ int mei_cl_disconnect(struct mei_cl *cl)
*
* @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);
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册