提交 06d2fe15 编写于 作者: L Linus Torvalds

Merge tag 'driver-core-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core

Pull driver core merge from Greg Kroah-Hartman:
 "Here is the big driver core update for 3.7-rc1.

  A number of firmware_class.c updates (as you saw a month or so ago),
  and some hyper-v updates and some printk fixes as well.  All patches
  that are outside of the drivers/base area have been acked by the
  respective maintainers, and have all been in the linux-next tree for a
  while.

  Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>"

* tag 'driver-core-3.6' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/driver-core: (95 commits)
  memory: tegra{20,30}-mc: Fix reading incorrect register in mc_readl()
  device.h: Add missing inline to #ifndef CONFIG_PRINTK dev_vprintk_emit
  memory: emif: Add ifdef CONFIG_DEBUG_FS guard for emif_debugfs_[init|exit]
  Documentation: Fixes some translation error in Documentation/zh_CN/gpio.txt
  Documentation: Remove 3 byte redundant code at the head of the Documentation/zh_CN/arm/booting
  Documentation: Chinese translation of Documentation/video4linux/omap3isp.txt
  device and dynamic_debug: Use dev_vprintk_emit and dev_printk_emit
  dev: Add dev_vprintk_emit and dev_printk_emit
  netdev_printk/netif_printk: Remove a superfluous logging colon
  netdev_printk/dynamic_netdev_dbg: Directly call printk_emit
  dev_dbg/dynamic_debug: Update to use printk_emit, optimize stack
  driver-core: Shut up dev_dbg_reatelimited() without DEBUG
  tools/hv: Parse /etc/os-release
  tools/hv: Check for read/write errors
  tools/hv: Fix exit() error code
  tools/hv: Fix file handle leak
  Tools: hv: Implement the KVP verb - KVP_OP_GET_IP_INFO
  Tools: hv: Rename the function kvp_get_ip_address()
  Tools: hv: Implement the KVP verb - KVP_OP_SET_IP_INFO
  Tools: hv: Add an example script to configure an interface
  ...
......@@ -13,7 +13,7 @@ Description:
accessory cables have such capability. For example,
the 30-pin port of Nuri board (/arch/arm/mach-exynos)
may have both HDMI and Charger attached, or analog audio,
video, and USB cables attached simulteneously.
video, and USB cables attached simultaneously.
If there are cables mutually exclusive with each other,
such binary relations may be expressed with extcon_dev's
......@@ -35,7 +35,7 @@ Description:
The /sys/class/extcon/.../state shows and stores the cable
attach/detach information of the corresponding extcon object.
If the extcon object has an optional callback "show_state"
defined, the showing function is overriden with the optional
defined, the showing function is overridden with the optional
callback.
If the default callback for showing function is used, the
......@@ -46,19 +46,19 @@ Description:
TA=1
EAR_JACK=0
#
In this example, the extcon device have USB_OTG and TA
In this example, the extcon device has USB_OTG and TA
cables attached and HDMI and EAR_JACK cables detached.
In order to update the state of an extcon device, enter a hex
state number starting with 0x.
echo 0xHEX > state
state number starting with 0x:
# echo 0xHEX > state
This updates the whole state of the extcon dev.
This updates the whole state of the extcon device.
Inputs of all the methods are required to meet the
mutually_exclusive contidions if they exist.
mutually_exclusive conditions if they exist.
It is recommended to use this "global" state interface if
you need to enter the value atomically. The later state
you need to set the value atomically. The later state
interface associated with each cable cannot update
multiple cable states of an extcon device simultaneously.
......@@ -73,7 +73,7 @@ What: /sys/class/extcon/.../cable.x/state
Date: February 2012
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
The /sys/class/extcon/.../cable.x/name shows and stores the
The /sys/class/extcon/.../cable.x/state shows and stores the
state of cable "x" (integer between 0 and 31) of an extcon
device. The state value is either 0 (detached) or 1
(attached).
......@@ -83,8 +83,8 @@ Date: December 2011
Contact: MyungJoo Ham <myungjoo.ham@samsung.com>
Description:
Shows the relations of mutually exclusiveness. For example,
if the mutually_exclusive array of extcon_dev is
{0x3, 0x5, 0xC, 0x0}, the, the output is:
if the mutually_exclusive array of extcon device is
{0x3, 0x5, 0xC, 0x0}, then the output is:
# ls mutually_exclusive/
0x3
0x5
......
......@@ -15,8 +15,8 @@ Debugfs is typically mounted with a command like:
mount -t debugfs none /sys/kernel/debug
(Or an equivalent /etc/fstab line).
The debugfs root directory is accessible by anyone by default. To
restrict access to the tree the "uid", "gid" and "mode" mount
The debugfs root directory is accessible only to the root user by
default. To change access to the tree the "uid", "gid" and "mode" mount
options can be used.
Note that the debugfs API is exported GPL-only to modules.
......
......@@ -284,9 +284,11 @@ instead, it is associated with the ktype. So let us introduce struct
kobj_type:
struct kobj_type {
void (*release)(struct kobject *);
void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
struct attribute **default_attrs;
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
};
This structure is used to describe a particular type of kobject (or, more
......
Chinese translated version of Documentation/arm/Booting
If you have any comment or update to the content, please contact the
original document maintainer directly. However, if you have a problem
communicating in English you can also ask the Chinese maintainer for
help. Contact the Chinese maintainer if this translation is outdated
or if there is a problem with the translation.
Maintainer: Russell King <linux@arm.linux.org.uk>
Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
---------------------------------------------------------------------
Documentation/arm/Booting 的中文翻译
如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
译存在问题,请联系中文版维护者。
英文版维护者: Russell King <linux@arm.linux.org.uk>
中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
以下为正文
---------------------------------------------------------------------
启动 ARM Linux
==============
作者:Russell King
日期:2002年5月18日
以下文档适用于 2.4.18-rmk6 及以上版本。
为了启动 ARM Linux,你需要一个引导装载程序(boot loader),
它是一个在主内核启动前运行的一个小程序。引导装载程序需要初始化各种
设备,并最终调用 Linux 内核,将信息传递给内核。
从本质上讲,引导装载程序应提供(至少)以下功能:
1、设置和初始化 RAM。
2、初始化一个串口。
3、检测机器的类型(machine type)。
4、设置内核标签列表(tagged list)。
5、调用内核映像。
1、设置和初始化 RAM
-------------------
现有的引导加载程序: 强制
新开发的引导加载程序: 强制
引导装载程序应该找到并初始化系统中所有内核用于保持系统变量数据的 RAM。
这个操作的执行是设备依赖的。(它可能使用内部算法来自动定位和计算所有
RAM,或可能使用对这个设备已知的 RAM 信息,还可能使用任何引导装载程序
设计者想到的匹配方法。)
2、初始化一个串口
-----------------------------
现有的引导加载程序: 可选、建议
新开发的引导加载程序: 可选、建议
引导加载程序应该初始化并使能一个目标板上的串口。这允许内核串口驱动
自动检测哪个串口用于内核控制台。(一般用于调试或与目标板通信。)
作为替代方案,引导加载程序也可以通过标签列表传递相关的'console='
选项给内核以指定某个串口,而串口数据格式的选项在以下文档中描述:
Documentation/kernel-parameters.txt。
3、检测机器类型
--------------------------
现有的引导加载程序: 可选
新开发的引导加载程序: 强制
引导加载程序应该通过某些方式检测自身所处的机器类型。这是一个硬件
代码或通过查看所连接的硬件用某些算法得到,这些超出了本文档的范围。
引导加载程序最终必须能提供一个 MACH_TYPE_xxx 值给内核。
(详见 linux/arch/arm/tools/mach-types )。
4、设置启动数据
------------------
现有的引导加载程序: 可选、强烈建议
新开发的引导加载程序: 强制
引导加载程序必须提供标签列表或者 dtb 映像以传递配置数据给内核。启动
数据的物理地址通过寄存器 r2 传递给内核。
4a、设置内核标签列表
--------------------------------
bootloader 必须创建和初始化内核标签列表。一个有效的标签列表以
ATAG_CORE 标签开始,并以 ATAG_NONE 标签结束。ATAG_CORE 标签可以是
空的,也可以是非空。一个空 ATAG_CORE 标签其 size 域设置为
‘2’(0x00000002)。ATAG_NONE 标签的 size 域必须设置为零。
在列表中可以保存任意数量的标签。对于一个重复的标签是追加到之前标签
所携带的信息之后,还是会覆盖原来的信息,是未定义的。某些标签的行为
是前者,其他是后者。
bootloader 必须传递一个系统内存的位置和最小值,以及根文件系统位置。
因此,最小的标签列表如下所示:
+-----------+
基地址 -> | ATAG_CORE | |
+-----------+ |
| ATAG_MEM | | 地址增长方向
+-----------+ |
| ATAG_NONE | |
+-----------+ v
标签列表应该保存在系统的 RAM 中。
标签列表必须置于内核自解压和 initrd'bootp' 程序都不会覆盖的内存区。
建议放在 RAM 的头 16KiB 中。
4b、设置设备树
-------------------------
bootloader 必须以 64bit 地址对齐的形式加载一个设备树映像(dtb)到系统
RAM 中,并用启动数据初始化它。dtb 格式在文档
Documentation/devicetree/booting-without-of.txt 中。内核将会在
dtb 物理地址处查找 dtb 魔数值(0xd00dfeed),以确定 dtb 是否已经代替
标签列表被传递进来。
bootloader 必须传递一个系统内存的位置和最小值,以及根文件系统位置。
dtb 必须置于内核自解压不会覆盖的内存区。建议将其放置于 RAM 的头 16KiB
中。但是不可将其放置于“0”物理地址处,因为内核认为:r2 中为 0,意味着
没有标签列表和 dtb 传递过来。
5、调用内核映像
---------------------------
现有的引导加载程序: 强制
新开发的引导加载程序: 强制
调用内核映像 zImage 有两个选择。如果 zImge 保存在 flash 中,且是为了
在 flash 中直接运行而被正确链接的。这样引导加载程序就可以在 flash 中
直接调用 zImage。
zImage 也可以被放在系统 RAM(任意位置)中被调用。注意:内核使用映像
基地址的前 16KB RAM 空间来保存页表。建议将映像置于 RAM 的 32KB 处。
对于以上任意一种情况,都必须符合以下启动状态:
- 停止所有 DMA 设备,这样内存数据就不会因为虚假网络包或磁盘数据而被破坏。
这可能可以节省你许多的调试时间。
- CPU 寄存器配置
r0 = 0,
r1 = (在上面 3 中获取的)机器类型码。
r2 = 标签列表在系统 RAM 中的物理地址,或
设备树块(dtb)在系统 RAM 中的物理地址
- CPU 模式
所有形式的中断必须被禁止 (IRQs 和 FIQs)
CPU 必须处于 SVC 模式。(对于 Angel 调试有特例存在)
- 缓存,MMUs
MMU 必须关闭。
指令缓存开启或关闭都可以。
数据缓存必须关闭。
- 引导加载程序应该通过直接跳转到内核映像的第一条指令来调用内核映像。
对于支持 ARM 指令集的 CPU,跳入内核入口时必须处在 ARM 状态,即使
对于 Thumb-2 内核也是如此。
对于仅支持 Thumb 指令集的 CPU,比如 Cortex-M 系列的 CPU,跳入
内核入口时必须处于 Thumb 状态。
Chinese translated version of Documentation/basic_profiling
If you have any comment or update to the content, please post to LKML directly.
However, if you have problem communicating in English you can also ask the
Chinese maintainer for help. Contact the Chinese maintainer, if this
translation is outdated or there is problem with translation.
Chinese maintainer: Liang Xie <xieliang@xiaomi.com>
---------------------------------------------------------------------
Documentation/basic_profiling的中文翻译
如果想评论或更新本文的内容,请直接发信到LKML。如果你使用英文交流有困难的话,也可
以向中文版维护者求助。如果本翻译更新不及时或者翻译存在问题,请联系中文版维护者。
中文版维护者: 谢良 Liang Xie <xieliang007@gmail.com>
中文版翻译者: 谢良 Liang Xie <xieliang007@gmail.com>
中文版校译者:
以下为正文
---------------------------------------------------------------------
下面这些说明指令都是非常基础的,如果你想进一步了解请阅读相关专业文档:)
请不要再在本文档增加新的内容,但可以修复文档中的错误:)(mbligh@aracnet.com)
感谢John Levon,Dave Hansen等在撰写时的帮助
<test> 用于表示要测量的目标
请先确保您已经有正确的System.map / vmlinux配置!
对于linux系统来说,配置vmlinuz最容易的方法可能就是使用“make install”,然后修改
/sbin/installkernel将vmlinux拷贝到/boot目录,而System.map通常是默认安装好的
Readprofile
-----------
2.6系列内核需要版本相对较新的readprofile,比如util-linux 2.12a中包含的,可以从:
http://www.kernel.org/pub/linux/utils/util-linux/ 下载
大部分linux发行版已经包含了.
启用readprofile需要在kernel启动命令行增加”profile=2“
clear readprofile -r
<test>
dump output readprofile -m /boot/System.map > captured_profile
Oprofile
--------
从http://oprofile.sourceforge.net/获取源代码(请参考Changes以获取匹配的版本)
在kernel启动命令行增加“idle=poll”
配置CONFIG_PROFILING=y和CONFIG_OPROFILE=y然后重启进入新kernel
./configure --with-kernel-support
make install
想得到好的测量结果,请确保启用了本地APIC特性。如果opreport显示有0Hz CPU,
说明APIC特性没有开启。另外注意idle=poll选项可能有损性能。
One time setup:
opcontrol --setup --vmlinux=/boot/vmlinux
clear opcontrol --reset
start opcontrol --start
<test>
stop opcontrol --stop
dump output opreport > output_file
如果只看kernel相关的报告结果,请运行命令 opreport -l /boot/vmlinux > output_file
通过reset选项可以清理过期统计数据,相当于重启的效果。
Chinese translated version of Documentation/filesystems/sysfs.txt
If you have any comment or update to the content, please contact the
original document maintainer directly. However, if you have a problem
communicating in English you can also ask the Chinese maintainer for
help. Contact the Chinese maintainer if this translation is outdated
or if there is a problem with the translation.
Maintainer: Patrick Mochel <mochel@osdl.org>
Mike Murphy <mamurph@cs.clemson.edu>
Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
---------------------------------------------------------------------
Documentation/filesystems/sysfs.txt 的中文翻译
如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
译存在问题,请联系中文版维护者。
英文版维护者: Patrick Mochel <mochel@osdl.org>
Mike Murphy <mamurph@cs.clemson.edu>
中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
以下为正文
---------------------------------------------------------------------
sysfs - 用于导出内核对象(kobject)的文件系统
Patrick Mochel <mochel@osdl.org>
Mike Murphy <mamurph@cs.clemson.edu>
修订: 16 August 2011
原始版本: 10 January 2003
sysfs 简介:
~~~~~~~~~~
sysfs 是一个最初基于 ramfs 且位于内存的文件系统。它提供导出内核
数据结构及其属性,以及它们之间的关联到用户空间的方法。
sysfs 始终与 kobject 的底层结构紧密相关。请阅读
Documentation/kobject.txt 文档以获得更多关于 kobject 接口的
信息。
使用 sysfs
~~~~~~~~~~~
只要内核配置中定义了 CONFIG_SYSFS ,sysfs 总是被编译进内核。你可
通过以下命令挂载它:
mount -t sysfs sysfs /sys
创建目录
~~~~~~~~
任何 kobject 在系统中注册,就会有一个目录在 sysfs 中被创建。这个
目录是作为该 kobject 的父对象所在目录的子目录创建的,以准确地传递
内核的对象层次到用户空间。sysfs 中的顶层目录代表着内核对象层次的
共同祖先;例如:某些对象属于某个子系统。
Sysfs 在与其目录关联的 sysfs_dirent 对象中内部保存一个指向实现
目录的 kobject 的指针。以前,这个 kobject 指针被 sysfs 直接用于
kobject 文件打开和关闭的引用计数。而现在的 sysfs 实现中,kobject
引用计数只能通过 sysfs_schedule_callback() 函数直接修改。
属性
~~~~
kobject 的属性可在文件系统中以普通文件的形式导出。Sysfs 为属性定义
了面向文件 I/O 操作的方法,以提供对内核属性的读写。
属性应为 ASCII 码文本文件。以一个文件只存储一个属性值为宜。但一个
文件只包含一个属性值可能影响效率,所以一个包含相同数据类型的属性值
数组也被广泛地接受。
混合类型、表达多行数据以及一些怪异的数据格式会遭到强烈反对。这样做是
很丢脸的,而且其代码会在未通知作者的情况下被重写。
一个简单的属性结构定义如下:
struct attribute {
char * name;
struct module *owner;
umode_t mode;
};
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr);
void sysfs_remove_file(struct kobject * kobj, const struct attribute * attr);
一个单独的属性结构并不包含读写其属性值的方法。子系统最好为增删特定
对象类型的属性定义自己的属性结构体和封装函数。
例如:驱动程序模型定义的 device_attribute 结构体如下:
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
int device_create_file(struct device *, const struct device_attribute *);
void device_remove_file(struct device *, const struct device_attribute *);
为了定义设备属性,同时定义了一下辅助宏:
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
例如:声明
static DEVICE_ATTR(foo, S_IWUSR | S_IRUGO, show_foo, store_foo);
等同于如下代码:
static struct device_attribute dev_attr_foo = {
.attr = {
.name = "foo",
.mode = S_IWUSR | S_IRUGO,
.show = show_foo,
.store = store_foo,
},
};
子系统特有的回调函数
~~~~~~~~~~~~~~~~~~~
当一个子系统定义一个新的属性类型时,必须实现一系列的 sysfs 操作,
以帮助读写调用实现属性所有者的显示和储存方法。
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *, char *);
ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};
[子系统应已经定义了一个 struct kobj_type 结构体作为这个类型的
描述符,并在此保存 sysfs_ops 的指针。更多的信息参见 kobject 的
文档]
sysfs 会为这个类型调用适当的方法。当一个文件被读写时,这个方法会
将一般的kobject 和 attribute 结构体指针转换为适当的指针类型后
调用相关联的函数。
示例:
#define to_dev(obj) container_of(obj, struct device, kobj)
#define to_dev_attr(_attr) container_of(_attr, struct device_attribute, attr)
static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct device_attribute *dev_attr = to_dev_attr(attr);
struct device *dev = to_dev(kobj);
ssize_t ret = -EIO;
if (dev_attr->show)
ret = dev_attr->show(dev, dev_attr, buf);
if (ret >= (ssize_t)PAGE_SIZE) {
print_symbol("dev_attr_show: %s returned bad count\n",
(unsigned long)dev_attr->show);
}
return ret;
}
读写属性数据
~~~~~~~~~~~~
在声明属性时,必须指定 show() 或 store() 方法,以实现属性的
读或写。这些方法的类型应该和以下的设备属性定义一样简单。
ssize_t (*show)(struct device *dev, struct device_attribute *attr, char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
也就是说,他们应只以一个处理对象、一个属性和一个缓冲指针作为参数。
sysfs 会分配一个大小为 (PAGE_SIZE) 的缓冲区并传递给这个方法。
Sysfs 将会为每次读写操作调用一次这个方法。这使得这些方法在执行时
会出现以下的行为:
- 在读方面(read(2)),show() 方法应该填充整个缓冲区。回想属性
应只导出了一个属性值或是一个同类型属性值的数组,所以这个代价将
不会不太高。
这使得用户空间可以局部地读和任意的向前搜索整个文件。如果用户空间
向后搜索到零或使用‘0’偏移执行一个pread(2)操作,show()方法将
再次被调用,以重新填充缓存。
- 在写方面(write(2)),sysfs 希望在第一次写操作时得到整个缓冲区。
之后 Sysfs 传递整个缓冲区给 store() 方法。
当要写 sysfs 文件时,用户空间进程应首先读取整个文件,修该想要
改变的值,然后回写整个缓冲区。
在读写属性值时,属性方法的执行应操作相同的缓冲区。
注记:
- 写操作导致的 show() 方法重载,会忽略当前文件位置。
- 缓冲区应总是 PAGE_SIZE 大小。对于i386,这个值为4096。
- show() 方法应该返回写入缓冲区的字节数,也就是 snprintf()的
返回值。
- show() 应始终使用 snprintf()。
- store() 应返回缓冲区的已用字节数。如果整个缓存都已填满,只需返回
count 参数。
- show() 或 store() 可以返回错误值。当得到一个非法值,必须返回一个
错误值。
- 一个传递给方法的对象将会通过 sysfs 调用对象内嵌的引用计数固定在
内存中。尽管如此,对象代表的物理实体(如设备)可能已不存在。如有必要,
应该实现一个检测机制。
一个简单的(未经实验证实的)设备属性实现如下:
static ssize_t show_name(struct device *dev, struct device_attribute *attr,
char *buf)
{
return scnprintf(buf, PAGE_SIZE, "%s\n", dev->name);
}
static ssize_t store_name(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
snprintf(dev->name, sizeof(dev->name), "%.*s",
(int)min(count, sizeof(dev->name) - 1), buf);
return count;
}
static DEVICE_ATTR(name, S_IRUGO, show_name, store_name);
(注意:真正的实现不允许用户空间设置设备名。)
顶层目录布局
~~~~~~~~~~~~
sysfs 目录的安排显示了内核数据结构之间的关系。
顶层 sysfs 目录如下:
block/
bus/
class/
dev/
devices/
firmware/
net/
fs/
devices/ 包含了一个设备树的文件系统表示。他直接映射了内部的内核
设备树,反映了设备的层次结构。
bus/ 包含了内核中各种总线类型的平面目录布局。每个总线目录包含两个
子目录:
devices/
drivers/
devices/ 包含了系统中出现的每个设备的符号链接,他们指向 root/ 下的
设备目录。
drivers/ 包含了每个已为特定总线上的设备而挂载的驱动程序的目录(这里
假定驱动没有跨越多个总线类型)。
fs/ 包含了一个为文件系统设立的目录。现在每个想要导出属性的文件系统必须
在 fs/ 下创建自己的层次结构(参见Documentation/filesystems/fuse.txt)。
dev/ 包含两个子目录: char/ 和 block/。在这两个子目录中,有以
<major>:<minor> 格式命名的符号链接。这些符号链接指向 sysfs 目录
中相应的设备。/sys/dev 提供一个通过一个 stat(2) 操作结果,查找
设备 sysfs 接口快捷的方法。
更多有关 driver-model 的特性信息可以在 Documentation/driver-model/
中找到。
TODO: 完成这一节。
当前接口
~~~~~~~~
以下的接口层普遍存在于当前的sysfs中:
- 设备 (include/linux/device.h)
----------------------------------
结构体:
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
声明:
DEVICE_ATTR(_name, _mode, _show, _store);
增/删属性:
int device_create_file(struct device *dev, const struct device_attribute * attr);
void device_remove_file(struct device *dev, const struct device_attribute * attr);
- 总线驱动程序 (include/linux/device.h)
--------------------------------------
结构体:
struct bus_attribute {
struct attribute attr;
ssize_t (*show)(struct bus_type *, char * buf);
ssize_t (*store)(struct bus_type *, const char * buf, size_t count);
};
声明:
BUS_ATTR(_name, _mode, _show, _store)
增/删属性:
int bus_create_file(struct bus_type *, struct bus_attribute *);
void bus_remove_file(struct bus_type *, struct bus_attribute *);
- 设备驱动程序 (include/linux/device.h)
-----------------------------------------
结构体:
struct driver_attribute {
struct attribute attr;
ssize_t (*show)(struct device_driver *, char * buf);
ssize_t (*store)(struct device_driver *, const char * buf,
size_t count);
};
声明:
DRIVER_ATTR(_name, _mode, _show, _store)
增/删属性:
int driver_create_file(struct device_driver *, const struct driver_attribute *);
void driver_remove_file(struct device_driver *, const struct driver_attribute *);
文档
~~~~
sysfs 目录结构以及其中包含的属性定义了一个内核与用户空间之间的 ABI。
对于任何 ABI,其自身的稳定和适当的文档是非常重要的。所有新的 sysfs
属性必须在 Documentation/ABI 中有文档。详见 Documentation/ABI/README。
此差异已折叠。
Chinese translated version of Documentation/video4linux/omap3isp.txt
If you have any comment or update to the content, please contact the
original document maintainer directly. However, if you have a problem
communicating in English you can also ask the Chinese maintainer for
help. Contact the Chinese maintainer if this translation is outdated
or if there is a problem with the translation.
Maintainer: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Sakari Ailus <sakari.ailus@iki.fi>
David Cohen <dacohen@gmail.com>
Chinese maintainer: Fu Wei <tekkamanninja@gmail.com>
---------------------------------------------------------------------
Documentation/video4linux/omap3isp.txt 的中文翻译
如果想评论或更新本文的内容,请直接联系原文档的维护者。如果你使用英文
交流有困难的话,也可以向中文版维护者求助。如果本翻译更新不及时或者翻
译存在问题,请联系中文版维护者。
英文版维护者: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Sakari Ailus <sakari.ailus@iki.fi>
David Cohen <dacohen@gmail.com>
中文版维护者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
中文版翻译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
中文版校译者: 傅炜 Fu Wei <tekkamanninja@gmail.com>
以下为正文
---------------------------------------------------------------------
OMAP 3 图像信号处理器 (ISP) 驱动
Copyright (C) 2010 Nokia Corporation
Copyright (C) 2009 Texas Instruments, Inc.
联系人: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Sakari Ailus <sakari.ailus@iki.fi>
David Cohen <dacohen@gmail.com>
介绍
===
本文档介绍了由 drivers/media/video/omap3isp 加载的德州仪器
(TI)OMAP 3 图像信号处理器 (ISP) 驱动。原始驱动由德州仪器(TI)
编写,但此后由诺基亚重写了两次。
驱动已在以下 OMAP 3 系列的芯片中成功使用:
3430
3530
3630
驱动实现了 V4L2、媒体控制器和 v4l2_subdev 接口。支持内核中使用
v4l2_subdev 接口的传感器、镜头和闪光灯驱动。
拆分为子设备
==========
OMAP 3 ISP 被拆分为 V4L2 子设备,ISP中的每个模块都由一个子设备
来表示。每个子设备向用户空间提供一个 V4L2 子设备接口。
OMAP3 ISP CCP2
OMAP3 ISP CSI2a
OMAP3 ISP CCDC
OMAP3 ISP preview
OMAP3 ISP resizer
OMAP3 ISP AEWB
OMAP3 ISP AF
OMAP3 ISP histogram
ISP 中每个可能的连接都通过一个链接嵌入到媒体控制器接口中。详见例程 [2]。
控制 OMAP 3 ISP
==============
通常,对 OMAP 3 ISP 的配置会在下一帧起始时生效。在传感器垂直消隐期间,
模块变为空闲时完成配置。在内存到内存的操作中,视频管道一次处理一帧。
应用配置应在帧间完成。
ISP 中的所有模块,除 CSI-2 和 (可能存在的)CCP2 接收器外,都必须
接收完整的帧数据。因此,传感器必须保证从不发送部分帧数据给ISP。
Autoidle(自动空闲)功能至少在 3430 的 ISP 模块中确实存在一些问题。
当 omap3isp 模块参数 autoidle 非零时,autoidle(自动空闲)功能
仅在 3630 中启用了。
事件机制
======
OMAP 3 ISP 驱动在 CCDC 和统计(AEWB、AF 和 直方图)子设备中支持
V4L2 事件机制接口。
CCDC 子设备通过 HS_VS 中断,处理 V4L2_EVENT_FRAME_SYNC 类型
事件,用于告知帧起始。早期版本的驱动则使用 V4L2_EVENT_OMAP3ISP_HS_VS。
当在 CCDC 模块中接收到起始帧的第一行时,会准确地触发事件。这个事件
可以在 CCDC 子设备中“订阅”。
(当使用并行接口时,必须注意正确地配置 VS 信号极性。而当使用串行接收时
这个会自动校正。)
每个统计子设备都可以产生事件。每当一个统计缓冲区可由用户空间应用程序
通过 VIDIOC_OMAP3ISP_STAT_REQ IOCTL 操作获取时,就会产生一个
事件。当前存在以下事件:
V4L2_EVENT_OMAP3ISP_AEWB
V4L2_EVENT_OMAP3ISP_AF
V4L2_EVENT_OMAP3ISP_HIST
这些 ioctl 的事件数据类型为 struct omap3isp_stat_event_status
结构体。如果出现计算错误的统计,也同样会产生一个事件,但没有相关的统计
数据缓冲区。这种情况下 omap3isp_stat_event_status.buf_err 会被
设置为非零值。
私有 IOCTL
==========
OMAP 3 ISP 驱动支持标准的 V4L2 IOCTL 以及可能存在且实用的控制。但
ISP 提供的许多功能都不在标准 IOCTL 之列,例如 gamma(伽马)表和统计
数据采集配置等。
通常,会有一个私有 ioctl 用于配置每个包含硬件依赖功能的模块。
支持以下私有 IOCTL:
VIDIOC_OMAP3ISP_CCDC_CFG
VIDIOC_OMAP3ISP_PRV_CFG
VIDIOC_OMAP3ISP_AEWB_CFG
VIDIOC_OMAP3ISP_HIST_CFG
VIDIOC_OMAP3ISP_AF_CFG
VIDIOC_OMAP3ISP_STAT_REQ
VIDIOC_OMAP3ISP_STAT_EN
在 include/linux/omap3isp.h 中描述了这些 ioctl 使用的参数结构体。
与特定 ISP 模块相关的 ISP 自身的详细功能在技术参考手册 (TRMs)中有
描述,详见文档结尾。
虽然在不使用任何私有 IOCTL 的情况下使用 ISP 驱动是可能的,但这样无法
获得最佳的图像质量。AEWB、AF 和 直方图(译者注:一般用于自动曝光和增益
控制,以及图像均衡等)模块无法在未使用适当的私有 IOCTL 配置的情况下使用。
CCDC 和 preview(预览)模块 IOCTL
===============================
VIDIOC_OMAP3ISP_CCDC_CFG 和 VIDIOC_OMAP3ISP_PRV_CFG IOCTL
被分别用于配置、启用和禁用 CCDC 和 preview(预览)模块的功能。在它们
所控制的模块中,两个 IOCTL 控制多种功能。VIDIOC_OMAP3ISP_CCDC_CFG IOCTL
接受一个指向 omap3isp_ccdc_update_config 结构体的指针作为它的参数。
同样的,VIDIOC_OMAP3ISP_PRV_CFG 接受一个指向 omap3isp_prev_update_config
结构体的指针。以上两个结构体定义位于 [1]。
这些结构体中的 update 域标识是否针对指定的功能更新配置,而 flag 域
则标识是启用还是禁用此功能。
update 和 flag 位接受以下掩码值。CCDC 和 preview(预览)模块的
每个单独功能都与一个 flag 关联(禁用或启用;在结构体中 flag 域的
一部分)和一个指向功能配置数据的指针。
对于 VIDIOC_OMAP3ISP_CCDC_CFG,下面列出了 update 和 flag 域
中的有效值。 这些值可能会在同一个 IOCTL 调用中配置多个功能。
OMAP3ISP_CCDC_ALAW
OMAP3ISP_CCDC_LPF
OMAP3ISP_CCDC_BLCLAMP
OMAP3ISP_CCDC_BCOMP
OMAP3ISP_CCDC_FPC
OMAP3ISP_CCDC_CULL
OMAP3ISP_CCDC_CONFIG_LSC
OMAP3ISP_CCDC_TBL_LSC
针对 VIDIOC_OMAP3ISP_PRV_CFG 的相应值如下:
OMAP3ISP_PREV_LUMAENH
OMAP3ISP_PREV_INVALAW
OMAP3ISP_PREV_HRZ_MED
OMAP3ISP_PREV_CFA
OMAP3ISP_PREV_CHROMA_SUPP
OMAP3ISP_PREV_WB
OMAP3ISP_PREV_BLKADJ
OMAP3ISP_PREV_RGB2RGB
OMAP3ISP_PREV_COLOR_CONV
OMAP3ISP_PREV_YC_LIMIT
OMAP3ISP_PREV_DEFECT_COR
OMAP3ISP_PREV_GAMMABYPASS
OMAP3ISP_PREV_DRK_FRM_CAPTURE
OMAP3ISP_PREV_DRK_FRM_SUBTRACT
OMAP3ISP_PREV_LENS_SHADING
OMAP3ISP_PREV_NF
OMAP3ISP_PREV_GAMMA
在启用某个功能的时候,相关的配置数据指针不可为 NULL。在禁用某个功能时,
配置数据指针会被忽略。
统计模块 IOCTL
=============
统计子设备相较于其他子设备提供了更多动态配置选项。在图像处理流水线处于
工作状态时,它们可以被启用、禁用和重配。
统计模块总是从 CCDC 中获取输入的图像数据(由于直方图内存读取未实现)。
统计数据可由用户通过统计子设备节点使用私有 IOCTL 获取。
AEWB、AF 和 直方图子设备提供的私有 IOCTL 极大程度上反应了 ISP 硬件
提供的寄存器级接口。有些方面纯粹和驱动程序的实现相关,这些将在下面讨论。
VIDIOC_OMAP3ISP_STAT_EN
-----------------------
这个私有 IOCTL 启用/禁用 一个统计模块。如果这个申请在视频流启动前完成,
它将在视频流水线开始工作时生效。如果视频流水线已经处于工作状态了,它将在
CCDC 变为空闲时生效。
VIDIOC_OMAP3ISP_AEWB_CFG, VIDIOC_OMAP3ISP_HIST_CFG and VIDIOC_OMAP3ISP_AF_CFG
-----------------------------------------------------------------------------
这些 IOCTL 用于配置模块。它们要求用户应用程序对硬件有深入的认识。对
大多数域的解释可以在 OMAP 的 TRM 中找到。以下两个域对于以上所有的
私有 IOCTL 配置都很常见,由于他们没有在 TRM 中提及,故需要对其有
更好的认识。
omap3isp_[h3a_af/h3a_aewb/hist]_config.buf_size:
模块在内部处理自身缓冲。对模块数据输出所必需的缓存大小依赖于已申请的配置。
虽然驱动支持在视频流工作时重新配置,但对于所需缓存量大于模块启用时内部
所分配数量的情况,则不支持重新配置。在这种情况下将返回 -EBUSY。为了避免
此类状况,无论是禁用/重配/启用模块,还是第一次配置时申请必须的缓存大小,
都应在模块禁用的情况下进行。
内部缓冲分配的大小需综合考虑所申请配置的最小缓存量以及 buf_size 域中
所设的值。如果 buf_size 域在[minimum(最小值), maximum(最大值)]
缓冲大小范围之外,则应该将其调整到其范围中。驱动则会选择最大值。正确的
buf_size 值将回写到用户应用程序中。
omap3isp_[h3a_af/h3a_aewb/hist]_config.config_counter:
由于配置并未在申请之后同步生效,驱动必须提供一个跟踪这类信息的方法,
以提供更准确的数据。在一个配置被申请之后,返回到用户空间应用程序的
config_counter 是一个与其配置相关的唯一值。当用户应用程序接收到
一个缓冲可用或一个新的缓冲申请事件时,这个 config_counter 用于
一个缓冲数据和一个配置的匹配。
VIDIOC_OMAP3ISP_STAT_REQ
------------------------
将内部缓冲队列中最早的数据发送到用户空间,然后丢弃此缓冲区。
omap3isp_stat_data.frame_number 域与视频缓冲的 field_count
域相匹配。
技术参考手册 (TRMs) 和其他文档
==========================
OMAP 3430 TRM:
<URL:http://focus.ti.com/pdfs/wtbu/OMAP34xx_ES3.1.x_PUBLIC_TRM_vZM.zip>
参考于 2011-03-05.
OMAP 35xx TRM:
<URL:http://www.ti.com/litv/pdf/spruf98o> 参考于 2011-03-05.
OMAP 3630 TRM:
<URL:http://focus.ti.com/pdfs/wtbu/OMAP36xx_ES1.x_PUBLIC_TRM_vQ.zip>
参考于 2011-03-05.
DM 3730 TRM:
<URL:http://www.ti.com/litv/pdf/sprugn4h> 参考于 2011-03-06.
参考资料
=======
[1] include/linux/omap3isp.h
[2] http://git.ideasonboard.org/?p=media-ctl.git;a=summary
此差异已折叠。
......@@ -2865,7 +2865,9 @@ F: include/linux/firewire*.h
F: tools/firewire/
FIRMWARE LOADER (request_firmware)
S: Orphan
M: Ming Lei <ming.lei@canonical.com>
L: linux-kernel@vger.kernel.org
S: Maintained
F: Documentation/firmware_class/
F: drivers/base/firmware*.c
F: include/linux/firmware.h
......
......@@ -412,14 +412,6 @@ config TILE_USB
config NEED_BOUNCE_POOL
def_bool USB_OHCI_HCD
config HOTPLUG
bool "Support for hot-pluggable devices"
---help---
Say Y here if you want to plug devices into your computer while
the system is running, and be able to use them quickly. In many
cases, the devices can likewise be unplugged at any time too.
One well-known example of this is USB.
source "drivers/pci/hotplug/Kconfig"
endmenu
......
......@@ -172,24 +172,6 @@ config CMDLINE
source "mm/Kconfig"
config HOTPLUG
bool "Support for hot-pluggable devices"
help
Say Y here if you want to plug devices into your computer while
the system is running, and be able to use them quickly. In many
cases, the devices can likewise be unplugged at any time too.
One well known example of this is PCMCIA- or PC-cards, credit-card
size devices such as network cards, modems or hard drives which are
plugged into slots found on all modern laptop computers. Another
example, used on modern desktops as well as laptops, is USB.
Enable HOTPLUG and build a modular kernel. Get agent software
(from <http://linux-hotplug.sourceforge.net/>) and install it.
Then your kernel will automatically call out to a user mode "policy
agent" (/sbin/hotplug) to load modules and set up software needed
to use devices as you hotplug them.
source "drivers/pcmcia/Kconfig"
source "drivers/pci/hotplug/Kconfig"
......
......@@ -184,6 +184,17 @@ static void device_release(struct kobject *kobj)
struct device *dev = kobj_to_dev(kobj);
struct device_private *p = dev->p;
/*
* Some platform devices are driven without driver attached
* and managed resources may have been acquired. Make sure
* all resources are released.
*
* Drivers still can add resources into device after device
* is deleted but alive, so release devres here to avoid
* possible memory leak.
*/
devres_release_all(dev);
if (dev->release)
dev->release(dev);
else if (dev->type && dev->type->release)
......@@ -1196,13 +1207,6 @@ void device_del(struct device *dev)
bus_remove_device(dev);
driver_deferred_probe_del(dev);
/*
* Some platform devices are driven without driver attached
* and managed resources may have been acquired. Make sure
* all resources are released.
*/
devres_release_all(dev);
/* Notify the platform of the removal, in case they
* need to do anything...
*/
......@@ -1861,26 +1865,20 @@ void device_shutdown(void)
*/
#ifdef CONFIG_PRINTK
int __dev_printk(const char *level, const struct device *dev,
struct va_format *vaf)
static int
create_syslog_header(const struct device *dev, char *hdr, size_t hdrlen)
{
char dict[128];
const char *level_extra = "";
size_t dictlen = 0;
const char *subsys;
if (!dev)
return printk("%s(NULL device *): %pV", level, vaf);
size_t pos = 0;
if (dev->class)
subsys = dev->class->name;
else if (dev->bus)
subsys = dev->bus->name;
else
goto skip;
return 0;
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
"SUBSYSTEM=%s", subsys);
pos += snprintf(hdr + pos, hdrlen - pos, "SUBSYSTEM=%s", subsys);
/*
* Add device identifier DEVICE=:
......@@ -1896,32 +1894,63 @@ int __dev_printk(const char *level, const struct device *dev,
c = 'b';
else
c = 'c';
dictlen++;
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
"DEVICE=%c%u:%u",
c, MAJOR(dev->devt), MINOR(dev->devt));
pos++;
pos += snprintf(hdr + pos, hdrlen - pos,
"DEVICE=%c%u:%u",
c, MAJOR(dev->devt), MINOR(dev->devt));
} else if (strcmp(subsys, "net") == 0) {
struct net_device *net = to_net_dev(dev);
dictlen++;
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
"DEVICE=n%u", net->ifindex);
pos++;
pos += snprintf(hdr + pos, hdrlen - pos,
"DEVICE=n%u", net->ifindex);
} else {
dictlen++;
dictlen += snprintf(dict + dictlen, sizeof(dict) - dictlen,
"DEVICE=+%s:%s", subsys, dev_name(dev));
pos++;
pos += snprintf(hdr + pos, hdrlen - pos,
"DEVICE=+%s:%s", subsys, dev_name(dev));
}
skip:
if (level[2])
level_extra = &level[2]; /* skip past KERN_SOH "L" */
return printk_emit(0, level[1] - '0',
dictlen ? dict : NULL, dictlen,
"%s %s: %s%pV",
dev_driver_string(dev), dev_name(dev),
level_extra, vaf);
return pos;
}
EXPORT_SYMBOL(create_syslog_header);
int dev_vprintk_emit(int level, const struct device *dev,
const char *fmt, va_list args)
{
char hdr[128];
size_t hdrlen;
hdrlen = create_syslog_header(dev, hdr, sizeof(hdr));
return vprintk_emit(0, level, hdrlen ? hdr : NULL, hdrlen, fmt, args);
}
EXPORT_SYMBOL(dev_vprintk_emit);
int dev_printk_emit(int level, const struct device *dev, const char *fmt, ...)
{
va_list args;
int r;
va_start(args, fmt);
r = dev_vprintk_emit(level, dev, fmt, args);
va_end(args);
return r;
}
EXPORT_SYMBOL(dev_printk_emit);
static int __dev_printk(const char *level, const struct device *dev,
struct va_format *vaf)
{
if (!dev)
return printk("%s(NULL device *): %pV", level, vaf);
return dev_printk_emit(level[1] - '0', dev,
"%s %s: %pV",
dev_driver_string(dev), dev_name(dev), vaf);
}
EXPORT_SYMBOL(__dev_printk);
int dev_printk(const char *level, const struct device *dev,
const char *fmt, ...)
......@@ -1936,6 +1965,7 @@ int dev_printk(const char *level, const struct device *dev,
vaf.va = &args;
r = __dev_printk(level, dev, &vaf);
va_end(args);
return r;
......@@ -1955,6 +1985,7 @@ int func(const struct device *dev, const char *fmt, ...) \
vaf.va = &args; \
\
r = __dev_printk(kern_level, dev, &vaf); \
\
va_end(args); \
\
return r; \
......
......@@ -143,6 +143,48 @@ void * devres_alloc(dr_release_t release, size_t size, gfp_t gfp)
EXPORT_SYMBOL_GPL(devres_alloc);
#endif
/**
* devres_for_each_res - Resource iterator
* @dev: Device to iterate resource from
* @release: Look for resources associated with this release function
* @match: Match function (optional)
* @match_data: Data for the match function
* @fn: Function to be called for each matched resource.
* @data: Data for @fn, the 3rd parameter of @fn
*
* Call @fn for each devres of @dev which is associated with @release
* and for which @match returns 1.
*
* RETURNS:
* void
*/
void devres_for_each_res(struct device *dev, dr_release_t release,
dr_match_t match, void *match_data,
void (*fn)(struct device *, void *, void *),
void *data)
{
struct devres_node *node;
struct devres_node *tmp;
unsigned long flags;
if (!fn)
return;
spin_lock_irqsave(&dev->devres_lock, flags);
list_for_each_entry_safe_reverse(node, tmp,
&dev->devres_head, entry) {
struct devres *dr = container_of(node, struct devres, node);
if (node->release != release)
continue;
if (match && !match(dev, dr->data, match_data))
continue;
fn(dev, dr->data, data);
}
spin_unlock_irqrestore(&dev->devres_lock, flags);
}
EXPORT_SYMBOL_GPL(devres_for_each_res);
/**
* devres_free - Free device resource data
* @res: Pointer to devres data to free
......
此差异已折叠。
......@@ -20,9 +20,13 @@
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/pm_runtime.h>
#include <linux/idr.h>
#include "base.h"
/* For automatically allocated device IDs */
static DEFINE_IDA(platform_devid_ida);
#define to_platform_driver(drv) (container_of((drv), struct platform_driver, \
driver))
......@@ -99,6 +103,9 @@ struct resource *platform_get_resource_byname(struct platform_device *dev,
for (i = 0; i < dev->num_resources; i++) {
struct resource *r = &dev->resource[i];
if (unlikely(!r->name))
continue;
if (type == resource_type(r) && !strcmp(r->name, name))
return r;
}
......@@ -263,7 +270,7 @@ EXPORT_SYMBOL_GPL(platform_device_add_data);
*/
int platform_device_add(struct platform_device *pdev)
{
int i, ret = 0;
int i, ret;
if (!pdev)
return -EINVAL;
......@@ -273,10 +280,27 @@ int platform_device_add(struct platform_device *pdev)
pdev->dev.bus = &platform_bus_type;
if (pdev->id != -1)
switch (pdev->id) {
default:
dev_set_name(&pdev->dev, "%s.%d", pdev->name, pdev->id);
else
break;
case PLATFORM_DEVID_NONE:
dev_set_name(&pdev->dev, "%s", pdev->name);
break;
case PLATFORM_DEVID_AUTO:
/*
* Automatically allocated device ID. We mark it as such so
* that we remember it must be freed, and we append a suffix
* to avoid namespace collision with explicit IDs.
*/
ret = ida_simple_get(&platform_devid_ida, 0, 0, GFP_KERNEL);
if (ret < 0)
goto err_out;
pdev->id = ret;
pdev->id_auto = true;
dev_set_name(&pdev->dev, "%s.%d.auto", pdev->name, pdev->id);
break;
}
for (i = 0; i < pdev->num_resources; i++) {
struct resource *p, *r = &pdev->resource[i];
......@@ -309,6 +333,11 @@ int platform_device_add(struct platform_device *pdev)
return ret;
failed:
if (pdev->id_auto) {
ida_simple_remove(&platform_devid_ida, pdev->id);
pdev->id = PLATFORM_DEVID_AUTO;
}
while (--i >= 0) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
......@@ -317,6 +346,7 @@ int platform_device_add(struct platform_device *pdev)
release_resource(r);
}
err_out:
return ret;
}
EXPORT_SYMBOL_GPL(platform_device_add);
......@@ -336,6 +366,11 @@ void platform_device_del(struct platform_device *pdev)
if (pdev) {
device_del(&pdev->dev);
if (pdev->id_auto) {
ida_simple_remove(&platform_devid_ida, pdev->id);
pdev->id = PLATFORM_DEVID_AUTO;
}
for (i = 0; i < pdev->num_resources; i++) {
struct resource *r = &pdev->resource[i];
unsigned long type = resource_type(r);
......
......@@ -1324,3 +1324,25 @@ int device_pm_wait_for_dev(struct device *subordinate, struct device *dev)
return async_error;
}
EXPORT_SYMBOL_GPL(device_pm_wait_for_dev);
/**
* dpm_for_each_dev - device iterator.
* @data: data for the callback.
* @fn: function to be called for each device.
*
* Iterate over devices in dpm_list, and call @fn for each device,
* passing it @data.
*/
void dpm_for_each_dev(void *data, void (*fn)(struct device *, void *))
{
struct device *dev;
if (!fn)
return;
device_pm_lock();
list_for_each_entry(dev, &dpm_list, power.entry)
fn(dev, data);
device_pm_unlock();
}
EXPORT_SYMBOL_GPL(dpm_for_each_dev);
......@@ -21,6 +21,12 @@ config EXTCON_GPIO
Say Y here to enable GPIO based extcon support. Note that GPIO
extcon supports single state per extcon instance.
config EXTCON_ADC_JACK
tristate "ADC Jack extcon support"
depends on IIO
help
Say Y here to enable extcon device driver based on ADC values.
config EXTCON_MAX77693
tristate "MAX77693 EXTCON Support"
depends on MFD_MAX77693
......@@ -41,7 +47,7 @@ config EXTCON_MAX8997
config EXTCON_ARIZONA
tristate "Wolfson Arizona EXTCON support"
depends on MFD_ARIZONA
depends on MFD_ARIZONA && INPUT
help
Say Y here to enable support for external accessory detection
with Wolfson Arizona devices. These are audio CODECs with
......
......@@ -2,8 +2,9 @@
# Makefile for external connector class (extcon) devices
#
obj-$(CONFIG_EXTCON) += extcon_class.o
obj-$(CONFIG_EXTCON_GPIO) += extcon_gpio.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_MAX77693) += extcon-max77693.o
obj-$(CONFIG_EXTCON_MAX8997) += extcon-max8997.o
obj-$(CONFIG_EXTCON_ARIZONA) += extcon-arizona.o
/*
* drivers/extcon/extcon-adc-jack.c
*
* Analog Jack extcon driver with ADC-based detection capability.
*
* Copyright (C) 2012 Samsung Electronics
* MyungJoo Ham <myungjoo.ham@samsung.com>
*
* Modified for calling to IIO to get adc by <anish.singh@samsung.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/workqueue.h>
#include <linux/iio/consumer.h>
#include <linux/extcon/extcon-adc-jack.h>
#include <linux/extcon.h>
/**
* struct adc_jack_data - internal data for adc_jack device driver
* @edev - extcon device.
* @cable_names - list of supported cables.
* @num_cables - size of cable_names.
* @adc_conditions - list of adc value conditions.
* @num_conditions - size of adc_conditions.
* @irq - irq number of attach/detach event (0 if not exist).
* @handling_delay - interrupt handler will schedule extcon event
* handling at handling_delay jiffies.
* @handler - extcon event handler called by interrupt handler.
* @chan - iio channel being queried.
*/
struct adc_jack_data {
struct extcon_dev edev;
const char **cable_names;
int num_cables;
struct adc_jack_cond *adc_conditions;
int num_conditions;
int irq;
unsigned long handling_delay; /* in jiffies */
struct delayed_work handler;
struct iio_channel *chan;
};
static void adc_jack_handler(struct work_struct *work)
{
struct adc_jack_data *data = container_of(to_delayed_work(work),
struct adc_jack_data,
handler);
u32 state = 0;
int ret, adc_val;
int i;
ret = iio_read_channel_raw(data->chan, &adc_val);
if (ret < 0) {
dev_err(data->edev.dev, "read channel() error: %d\n", ret);
return;
}
/* Get state from adc value with adc_conditions */
for (i = 0; i < data->num_conditions; i++) {
struct adc_jack_cond *def = &data->adc_conditions[i];
if (!def->state)
break;
if (def->min_adc <= adc_val && def->max_adc >= adc_val) {
state = def->state;
break;
}
}
/* if no def has met, it means state = 0 (no cables attached) */
extcon_set_state(&data->edev, state);
}
static irqreturn_t adc_jack_irq_thread(int irq, void *_data)
{
struct adc_jack_data *data = _data;
schedule_delayed_work(&data->handler, data->handling_delay);
return IRQ_HANDLED;
}
static int __devinit adc_jack_probe(struct platform_device *pdev)
{
struct adc_jack_data *data;
struct adc_jack_pdata *pdata = pdev->dev.platform_data;
int i, err = 0;
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
if (!data)
return -ENOMEM;
data->edev.name = pdata->name;
if (!pdata->cable_names) {
err = -EINVAL;
dev_err(&pdev->dev, "error: cable_names not defined.\n");
goto out;
}
data->edev.supported_cable = pdata->cable_names;
/* Check the length of array and set num_cables */
for (i = 0; data->edev.supported_cable[i]; i++)
;
if (i == 0 || i > SUPPORTED_CABLE_MAX) {
err = -EINVAL;
dev_err(&pdev->dev, "error: pdata->cable_names size = %d\n",
i - 1);
goto out;
}
data->num_cables = i;
if (!pdata->adc_conditions ||
!pdata->adc_conditions[0].state) {
err = -EINVAL;
dev_err(&pdev->dev, "error: adc_conditions not defined.\n");
goto out;
}
data->adc_conditions = pdata->adc_conditions;
/* Check the length of array and set num_conditions */
for (i = 0; data->adc_conditions[i].state; i++)
;
data->num_conditions = i;
data->chan = iio_channel_get(dev_name(&pdev->dev),
pdata->consumer_channel);
if (IS_ERR(data->chan)) {
err = PTR_ERR(data->chan);
goto out;
}
data->handling_delay = msecs_to_jiffies(pdata->handling_delay_ms);
INIT_DELAYED_WORK_DEFERRABLE(&data->handler, adc_jack_handler);
platform_set_drvdata(pdev, data);
err = extcon_dev_register(&data->edev, &pdev->dev);
if (err)
goto out;
data->irq = platform_get_irq(pdev, 0);
if (!data->irq) {
dev_err(&pdev->dev, "platform_get_irq failed\n");
err = -ENODEV;
goto err_irq;
}
err = request_any_context_irq(data->irq, adc_jack_irq_thread,
pdata->irq_flags, pdata->name, data);
if (err) {
dev_err(&pdev->dev, "error: irq %d\n", data->irq);
err = -EINVAL;
goto err_irq;
}
goto out;
err_irq:
extcon_dev_unregister(&data->edev);
out:
return err;
}
static int __devexit adc_jack_remove(struct platform_device *pdev)
{
struct adc_jack_data *data = platform_get_drvdata(pdev);
free_irq(data->irq, data);
cancel_work_sync(&data->handler.work);
extcon_dev_unregister(&data->edev);
return 0;
}
static struct platform_driver adc_jack_driver = {
.probe = adc_jack_probe,
.remove = __devexit_p(adc_jack_remove),
.driver = {
.name = "adc-jack",
.owner = THIS_MODULE,
},
};
module_platform_driver(adc_jack_driver);
......@@ -21,6 +21,7 @@
#include <linux/interrupt.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/input.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/regulator/consumer.h>
......@@ -30,11 +31,14 @@
#include <linux/mfd/arizona/pdata.h>
#include <linux/mfd/arizona/registers.h>
#define ARIZONA_NUM_BUTTONS 6
struct arizona_extcon_info {
struct device *dev;
struct arizona *arizona;
struct mutex lock;
struct regulator *micvdd;
struct input_dev *input;
int micd_mode;
const struct arizona_micd_config *micd_modes;
......@@ -54,6 +58,18 @@ static const struct arizona_micd_config micd_default_modes[] = {
{ 0, 2 << ARIZONA_MICD_BIAS_SRC_SHIFT, 1 },
};
static struct {
u16 status;
int report;
} arizona_lvl_to_key[ARIZONA_NUM_BUTTONS] = {
{ 0x1, BTN_0 },
{ 0x2, BTN_1 },
{ 0x4, BTN_2 },
{ 0x8, BTN_3 },
{ 0x10, BTN_4 },
{ 0x20, BTN_5 },
};
#define ARIZONA_CABLE_MECHANICAL 0
#define ARIZONA_CABLE_MICROPHONE 1
#define ARIZONA_CABLE_HEADPHONE 2
......@@ -133,6 +149,7 @@ static void arizona_stop_mic(struct arizona_extcon_info *info)
if (change) {
regulator_disable(info->micvdd);
pm_runtime_mark_last_busy(info->dev);
pm_runtime_put_autosuspend(info->dev);
}
}
......@@ -141,8 +158,8 @@ static irqreturn_t arizona_micdet(int irq, void *data)
{
struct arizona_extcon_info *info = data;
struct arizona *arizona = info->arizona;
unsigned int val;
int ret;
unsigned int val, lvl;
int ret, i;
mutex_lock(&info->lock);
......@@ -219,13 +236,22 @@ static irqreturn_t arizona_micdet(int irq, void *data)
/*
* If we're still detecting and we detect a short then we've
* got a headphone. Otherwise it's a button press, the
* button reporting is stubbed out for now.
* got a headphone. Otherwise it's a button press.
*/
if (val & 0x3fc) {
if (info->mic) {
dev_dbg(arizona->dev, "Mic button detected\n");
lvl = val & ARIZONA_MICD_LVL_MASK;
lvl >>= ARIZONA_MICD_LVL_SHIFT;
for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
if (lvl & arizona_lvl_to_key[i].status)
input_report_key(info->input,
arizona_lvl_to_key[i].report,
1);
input_sync(info->input);
} else if (info->detecting) {
dev_dbg(arizona->dev, "Headphone detected\n");
info->detecting = false;
......@@ -244,6 +270,10 @@ static irqreturn_t arizona_micdet(int irq, void *data)
}
} else {
dev_dbg(arizona->dev, "Mic button released\n");
for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
input_report_key(info->input,
arizona_lvl_to_key[i].report, 0);
input_sync(info->input);
}
handled:
......@@ -258,7 +288,7 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
struct arizona_extcon_info *info = data;
struct arizona *arizona = info->arizona;
unsigned int val;
int ret;
int ret, i;
pm_runtime_get_sync(info->dev);
......@@ -288,6 +318,11 @@ static irqreturn_t arizona_jackdet(int irq, void *data)
arizona_stop_mic(info);
for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
input_report_key(info->input,
arizona_lvl_to_key[i].report, 0);
input_sync(info->input);
ret = extcon_update_state(&info->edev, 0xffffffff, 0);
if (ret != 0)
dev_err(arizona->dev, "Removal report failed: %d\n",
......@@ -307,13 +342,13 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
struct arizona_pdata *pdata;
struct arizona_extcon_info *info;
int ret, mode;
int ret, mode, i;
pdata = dev_get_platdata(arizona->dev);
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
if (!info) {
dev_err(&pdev->dev, "failed to allocate memory\n");
dev_err(&pdev->dev, "Failed to allocate memory\n");
ret = -ENOMEM;
goto err;
}
......@@ -350,7 +385,7 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
ret = extcon_dev_register(&info->edev, arizona->dev);
if (ret < 0) {
dev_err(arizona->dev, "extcon_dev_regster() failed: %d\n",
dev_err(arizona->dev, "extcon_dev_register() failed: %d\n",
ret);
goto err;
}
......@@ -382,6 +417,20 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
arizona_extcon_set_mode(info, 0);
info->input = input_allocate_device();
if (!info->input) {
dev_err(arizona->dev, "Can't allocate input dev\n");
ret = -ENOMEM;
goto err_register;
}
for (i = 0; i < ARIZONA_NUM_BUTTONS; i++)
input_set_capability(info->input, EV_KEY,
arizona_lvl_to_key[i].report);
info->input->name = "Headset";
info->input->phys = "arizona/extcon";
info->input->dev.parent = &pdev->dev;
pm_runtime_enable(&pdev->dev);
pm_runtime_idle(&pdev->dev);
pm_runtime_get_sync(&pdev->dev);
......@@ -391,7 +440,7 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
if (ret != 0) {
dev_err(&pdev->dev, "Failed to get JACKDET rise IRQ: %d\n",
ret);
goto err_register;
goto err_input;
}
ret = arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 1);
......@@ -441,8 +490,16 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
pm_runtime_put(&pdev->dev);
ret = input_register_device(info->input);
if (ret) {
dev_err(&pdev->dev, "Can't register input device: %d\n", ret);
goto err_micdet;
}
return 0;
err_micdet:
arizona_free_irq(arizona, ARIZONA_IRQ_MICDET, info);
err_fall_wake:
arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_FALL, 0);
err_fall:
......@@ -451,6 +508,8 @@ static int __devinit arizona_extcon_probe(struct platform_device *pdev)
arizona_set_irq_wake(arizona, ARIZONA_IRQ_JD_RISE, 0);
err_rise:
arizona_free_irq(arizona, ARIZONA_IRQ_JD_RISE, info);
err_input:
input_free_device(info->input);
err_register:
pm_runtime_disable(&pdev->dev);
extcon_dev_unregister(&info->edev);
......@@ -473,6 +532,7 @@ static int __devexit arizona_extcon_remove(struct platform_device *pdev)
regmap_update_bits(arizona->regmap, ARIZONA_JACK_DETECT_ANALOGUE,
ARIZONA_JD1_ENA, 0);
arizona_clk32k_disable(arizona);
input_unregister_device(info->input);
extcon_dev_unregister(&info->edev);
return 0;
......
......@@ -30,6 +30,7 @@
#include <linux/err.h>
#include <linux/extcon.h>
#include <linux/slab.h>
#include <linux/sysfs.h>
/*
* extcon_cable_name suggests the standard cable names for commonly used
......@@ -442,7 +443,7 @@ static int _call_per_cable(struct notifier_block *nb, unsigned long val,
/**
* extcon_register_interest() - Register a notifier for a state change of a
* specific cable, not a entier set of cables of a
* specific cable, not an entier set of cables of a
* extcon device.
* @obj: an empty extcon_specific_cable_nb object to be returned.
* @extcon_name: the name of extcon device.
......@@ -498,7 +499,7 @@ int extcon_unregister_interest(struct extcon_specific_cable_nb *obj)
}
/**
* extcon_register_notifier() - Register a notifee to get notified by
* extcon_register_notifier() - Register a notifiee to get notified by
* any attach status changes from the extcon.
* @edev: the extcon device.
* @nb: a notifier block to be registered.
......@@ -515,7 +516,7 @@ int extcon_register_notifier(struct extcon_dev *edev,
EXPORT_SYMBOL_GPL(extcon_register_notifier);
/**
* extcon_unregister_notifier() - Unregister a notifee from the extcon device.
* extcon_unregister_notifier() - Unregister a notifiee from the extcon device.
* @edev: the extcon device.
* @nb: a registered notifier block to be unregistered.
*/
......@@ -673,10 +674,12 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
cable->attr_g.name = str;
cable->attr_g.attrs = cable->attrs;
sysfs_attr_init(&cable->attr_name.attr);
cable->attr_name.attr.name = "name";
cable->attr_name.attr.mode = 0444;
cable->attr_name.show = cable_name_show;
sysfs_attr_init(&cable->attr_state.attr);
cable->attr_state.attr.name = "state";
cable->attr_state.attr.mode = 0644;
cable->attr_state.show = cable_state_show;
......@@ -722,6 +725,7 @@ int extcon_dev_register(struct extcon_dev *edev, struct device *dev)
goto err_muex;
}
strcpy(name, buf);
sysfs_attr_init(&edev->d_attrs_muex[index].attr);
edev->d_attrs_muex[index].attr.name = name;
edev->d_attrs_muex[index].attr.mode = 0000;
edev->attrs_muex[index] = &edev->d_attrs_muex[index]
......@@ -802,7 +806,7 @@ EXPORT_SYMBOL_GPL(extcon_dev_register);
/**
* extcon_dev_unregister() - Unregister the extcon device.
* @edev: the extcon device instance to be unregitered.
* @edev: the extcon device instance to be unregistered.
*
* Note that this does not call kfree(edev) because edev was not allocated
* by this class.
......
......@@ -356,7 +356,7 @@ static int max77693_muic_adc_ground_handler(struct max77693_muic_info *info,
extcon_set_cable_state(info->edev, "MHL", attached);
break;
default:
dev_err(info->dev, "faild to detect %s accessory\n",
dev_err(info->dev, "failed to detect %s accessory\n",
attached ? "attached" : "detached");
dev_err(info->dev, "- adc:0x%x, adclow:0x%x, adc1k:0x%x\n",
adc, adclow, adc1k);
......@@ -548,7 +548,7 @@ static void max77693_muic_irq_work(struct work_struct *work)
curr_adc = info->status[0] & STATUS1_ADC_MASK;
curr_adc >>= STATUS1_ADC_SHIFT;
/* Check accossory state which is either detached or attached */
/* Check accessory state which is either detached or attached */
if (curr_adc == MAX77693_MUIC_ADC_OPEN)
attached = false;
......@@ -564,7 +564,7 @@ static void max77693_muic_irq_work(struct work_struct *work)
curr_chg_type = info->status[1] & STATUS2_CHGTYP_MASK;
curr_chg_type >>= STATUS2_CHGTYP_SHIFT;
/* Check charger accossory state which
/* Check charger accessory state which
is either detached or attached */
if (curr_chg_type == MAX77693_CHARGER_TYPE_NONE)
attached = false;
......@@ -699,7 +699,7 @@ static int __devinit max77693_muic_probe(struct platform_device *pdev)
ret = request_threaded_irq(virq, NULL,
max77693_muic_irq_handler,
0, muic_irq->name, info);
IRQF_ONESHOT, muic_irq->name, info);
if (ret) {
dev_err(&pdev->dev,
"failed: irq request (IRQ: %d,"
......
......@@ -435,12 +435,23 @@ efivar_attr_read(struct efivar_entry *entry, char *buf)
if (status != EFI_SUCCESS)
return -EIO;
if (var->Attributes & 0x1)
if (var->Attributes & EFI_VARIABLE_NON_VOLATILE)
str += sprintf(str, "EFI_VARIABLE_NON_VOLATILE\n");
if (var->Attributes & 0x2)
if (var->Attributes & EFI_VARIABLE_BOOTSERVICE_ACCESS)
str += sprintf(str, "EFI_VARIABLE_BOOTSERVICE_ACCESS\n");
if (var->Attributes & 0x4)
if (var->Attributes & EFI_VARIABLE_RUNTIME_ACCESS)
str += sprintf(str, "EFI_VARIABLE_RUNTIME_ACCESS\n");
if (var->Attributes & EFI_VARIABLE_HARDWARE_ERROR_RECORD)
str += sprintf(str, "EFI_VARIABLE_HARDWARE_ERROR_RECORD\n");
if (var->Attributes & EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS)
str += sprintf(str,
"EFI_VARIABLE_AUTHENTICATED_WRITE_ACCESS\n");
if (var->Attributes &
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS)
str += sprintf(str,
"EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS\n");
if (var->Attributes & EFI_VARIABLE_APPEND_WRITE)
str += sprintf(str, "EFI_VARIABLE_APPEND_WRITE\n");
return str - buf;
}
......
......@@ -26,6 +26,7 @@
#include <linux/slab.h>
#include <linux/vmalloc.h>
#include <linux/hyperv.h>
#include <linux/version.h>
#include <asm/hyperv.h>
#include "hyperv_vmbus.h"
......@@ -37,28 +38,6 @@ struct hv_context hv_context = {
.signal_event_buffer = NULL,
};
/*
* query_hypervisor_presence
* - Query the cpuid for presence of windows hypervisor
*/
static int query_hypervisor_presence(void)
{
unsigned int eax;
unsigned int ebx;
unsigned int ecx;
unsigned int edx;
unsigned int op;
eax = 0;
ebx = 0;
ecx = 0;
edx = 0;
op = HVCPUID_VERSION_FEATURES;
cpuid(op, &eax, &ebx, &ecx, &edx);
return ecx & HV_PRESENT_BIT;
}
/*
* query_hypervisor_info - Get version info of the windows hypervisor
*/
......@@ -159,14 +138,13 @@ int hv_init(void)
memset(hv_context.synic_message_page, 0,
sizeof(void *) * NR_CPUS);
if (!query_hypervisor_presence())
goto cleanup;
max_leaf = query_hypervisor_info();
/* Write our OS info */
wrmsrl(HV_X64_MSR_GUEST_OS_ID, HV_LINUX_GUEST_ID);
hv_context.guestid = HV_LINUX_GUEST_ID;
/*
* Write our OS ID.
*/
hv_context.guestid = generate_guest_id(0, LINUX_VERSION_CODE, 0);
wrmsrl(HV_X64_MSR_GUEST_OS_ID, hv_context.guestid);
/* See if the hypercall page is already set */
rdmsrl(HV_X64_MSR_HYPERCALL, hypercall_msr.as_uint64);
......
此差异已折叠。
......@@ -263,7 +263,7 @@ static int util_probe(struct hv_device *dev,
(struct hv_util_service *)dev_id->driver_data;
int ret;
srv->recv_buffer = kmalloc(PAGE_SIZE, GFP_KERNEL);
srv->recv_buffer = kmalloc(PAGE_SIZE * 2, GFP_KERNEL);
if (!srv->recv_buffer)
return -ENOMEM;
if (srv->util_init) {
......@@ -274,7 +274,7 @@ static int util_probe(struct hv_device *dev,
}
}
ret = vmbus_open(dev->channel, 2 * PAGE_SIZE, 2 * PAGE_SIZE, NULL, 0,
ret = vmbus_open(dev->channel, 4 * PAGE_SIZE, 4 * PAGE_SIZE, NULL, 0,
srv->util_cb, dev->channel);
if (ret)
goto error;
......
......@@ -410,10 +410,49 @@ enum {
#define HV_PRESENT_BIT 0x80000000
#define HV_LINUX_GUEST_ID_LO 0x00000000
#define HV_LINUX_GUEST_ID_HI 2976579765
#define HV_LINUX_GUEST_ID (((u64)HV_LINUX_GUEST_ID_HI << 32) | \
HV_LINUX_GUEST_ID_LO)
/*
* The guest OS needs to register the guest ID with the hypervisor.
* The guest ID is a 64 bit entity and the structure of this ID is
* specified in the Hyper-V specification:
*
* http://msdn.microsoft.com/en-us/library/windows/hardware/ff542653%28v=vs.85%29.aspx
*
* While the current guideline does not specify how Linux guest ID(s)
* need to be generated, our plan is to publish the guidelines for
* Linux and other guest operating systems that currently are hosted
* on Hyper-V. The implementation here conforms to this yet
* unpublished guidelines.
*
*
* Bit(s)
* 63 - Indicates if the OS is Open Source or not; 1 is Open Source
* 62:56 - Os Type; Linux is 0x100
* 55:48 - Distro specific identification
* 47:16 - Linux kernel version number
* 15:0 - Distro specific identification
*
*
*/
#define HV_LINUX_VENDOR_ID 0x8100
/*
* Generate the guest ID based on the guideline described above.
*/
static inline __u64 generate_guest_id(__u8 d_info1, __u32 kernel_version,
__u16 d_info2)
{
__u64 guest_id = 0;
guest_id = (((__u64)HV_LINUX_VENDOR_ID) << 48);
guest_id |= (((__u64)(d_info1)) << 48);
guest_id |= (((__u64)(kernel_version)) << 16);
guest_id |= ((__u64)(d_info2));
return guest_id;
}
#define HV_CPU_POWER_MANAGEMENT (1 << 0)
#define HV_RECOMMENDATIONS_MAX 4
......
......@@ -34,6 +34,7 @@
#include <linux/completion.h>
#include <linux/hyperv.h>
#include <asm/hyperv.h>
#include <asm/hypervisor.h>
#include "hyperv_vmbus.h"
......@@ -146,43 +147,9 @@ static ssize_t vmbus_show_device_attr(struct device *dev,
get_channel_info(hv_dev, device_info);
if (!strcmp(dev_attr->attr.name, "class_id")) {
ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
"%02x%02x%02x%02x%02x%02x%02x%02x}\n",
device_info->chn_type.b[3],
device_info->chn_type.b[2],
device_info->chn_type.b[1],
device_info->chn_type.b[0],
device_info->chn_type.b[5],
device_info->chn_type.b[4],
device_info->chn_type.b[7],
device_info->chn_type.b[6],
device_info->chn_type.b[8],
device_info->chn_type.b[9],
device_info->chn_type.b[10],
device_info->chn_type.b[11],
device_info->chn_type.b[12],
device_info->chn_type.b[13],
device_info->chn_type.b[14],
device_info->chn_type.b[15]);
ret = sprintf(buf, "{%pUl}\n", device_info->chn_type.b);
} else if (!strcmp(dev_attr->attr.name, "device_id")) {
ret = sprintf(buf, "{%02x%02x%02x%02x-%02x%02x-%02x%02x-"
"%02x%02x%02x%02x%02x%02x%02x%02x}\n",
device_info->chn_instance.b[3],
device_info->chn_instance.b[2],
device_info->chn_instance.b[1],
device_info->chn_instance.b[0],
device_info->chn_instance.b[5],
device_info->chn_instance.b[4],
device_info->chn_instance.b[7],
device_info->chn_instance.b[6],
device_info->chn_instance.b[8],
device_info->chn_instance.b[9],
device_info->chn_instance.b[10],
device_info->chn_instance.b[11],
device_info->chn_instance.b[12],
device_info->chn_instance.b[13],
device_info->chn_instance.b[14],
device_info->chn_instance.b[15]);
ret = sprintf(buf, "{%pUl}\n", device_info->chn_instance.b);
} else if (!strcmp(dev_attr->attr.name, "modalias")) {
print_alias_name(hv_dev, alias_name);
ret = sprintf(buf, "vmbus:%s\n", alias_name);
......@@ -757,6 +724,9 @@ static int __init hv_acpi_init(void)
{
int ret, t;
if (x86_hyper != &x86_hyper_ms_hyperv)
return -ENODEV;
init_completion(&probe_event);
/*
......
......@@ -2,6 +2,9 @@
# Makefile for memory devices
#
ifeq ($(CONFIG_DDR),y)
obj-$(CONFIG_OF) += of_memory.o
endif
obj-$(CONFIG_TI_EMIF) += emif.o
obj-$(CONFIG_TEGRA20_MC) += tegra20-mc.o
obj-$(CONFIG_TEGRA30_MC) += tegra30-mc.o
此差异已折叠。
/*
* OpenFirmware helpers for memory drivers
*
* Copyright (C) 2012 Texas Instruments, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/list.h>
#include <linux/of.h>
#include <linux/gfp.h>
#include <memory/jedec_ddr.h>
#include <linux/export.h>
/**
* of_get_min_tck() - extract min timing values for ddr
* @np: pointer to ddr device tree node
* @device: device requesting for min timing values
*
* Populates the lpddr2_min_tck structure by extracting data
* from device tree node. Returns a pointer to the populated
* structure. If any error in populating the structure, returns
* default min timings provided by JEDEC.
*/
const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
struct device *dev)
{
int ret = 0;
struct lpddr2_min_tck *min;
min = devm_kzalloc(dev, sizeof(*min), GFP_KERNEL);
if (!min)
goto default_min_tck;
ret |= of_property_read_u32(np, "tRPab-min-tck", &min->tRPab);
ret |= of_property_read_u32(np, "tRCD-min-tck", &min->tRCD);
ret |= of_property_read_u32(np, "tWR-min-tck", &min->tWR);
ret |= of_property_read_u32(np, "tRASmin-min-tck", &min->tRASmin);
ret |= of_property_read_u32(np, "tRRD-min-tck", &min->tRRD);
ret |= of_property_read_u32(np, "tWTR-min-tck", &min->tWTR);
ret |= of_property_read_u32(np, "tXP-min-tck", &min->tXP);
ret |= of_property_read_u32(np, "tRTP-min-tck", &min->tRTP);
ret |= of_property_read_u32(np, "tCKE-min-tck", &min->tCKE);
ret |= of_property_read_u32(np, "tCKESR-min-tck", &min->tCKESR);
ret |= of_property_read_u32(np, "tFAW-min-tck", &min->tFAW);
if (ret) {
devm_kfree(dev, min);
goto default_min_tck;
}
return min;
default_min_tck:
dev_warn(dev, "%s: using default min-tck values\n", __func__);
return &lpddr2_jedec_min_tck;
}
EXPORT_SYMBOL(of_get_min_tck);
static int of_do_get_timings(struct device_node *np,
struct lpddr2_timings *tim)
{
int ret;
ret = of_property_read_u32(np, "max-freq", &tim->max_freq);
ret |= of_property_read_u32(np, "min-freq", &tim->min_freq);
ret |= of_property_read_u32(np, "tRPab", &tim->tRPab);
ret |= of_property_read_u32(np, "tRCD", &tim->tRCD);
ret |= of_property_read_u32(np, "tWR", &tim->tWR);
ret |= of_property_read_u32(np, "tRAS-min", &tim->tRAS_min);
ret |= of_property_read_u32(np, "tRRD", &tim->tRRD);
ret |= of_property_read_u32(np, "tWTR", &tim->tWTR);
ret |= of_property_read_u32(np, "tXP", &tim->tXP);
ret |= of_property_read_u32(np, "tRTP", &tim->tRTP);
ret |= of_property_read_u32(np, "tCKESR", &tim->tCKESR);
ret |= of_property_read_u32(np, "tDQSCK-max", &tim->tDQSCK_max);
ret |= of_property_read_u32(np, "tFAW", &tim->tFAW);
ret |= of_property_read_u32(np, "tZQCS", &tim->tZQCS);
ret |= of_property_read_u32(np, "tZQCL", &tim->tZQCL);
ret |= of_property_read_u32(np, "tZQinit", &tim->tZQinit);
ret |= of_property_read_u32(np, "tRAS-max-ns", &tim->tRAS_max_ns);
ret |= of_property_read_u32(np, "tDQSCK-max-derated",
&tim->tDQSCK_max_derated);
return ret;
}
/**
* of_get_ddr_timings() - extracts the ddr timings and updates no of
* frequencies available.
* @np_ddr: Pointer to ddr device tree node
* @dev: Device requesting for ddr timings
* @device_type: Type of ddr(LPDDR2 S2/S4)
* @nr_frequencies: No of frequencies available for ddr
* (updated by this function)
*
* Populates lpddr2_timings structure by extracting data from device
* tree node. Returns pointer to populated structure. If any error
* while populating, returns default timings provided by JEDEC.
*/
const struct lpddr2_timings *of_get_ddr_timings(struct device_node *np_ddr,
struct device *dev, u32 device_type, u32 *nr_frequencies)
{
struct lpddr2_timings *timings = NULL;
u32 arr_sz = 0, i = 0;
struct device_node *np_tim;
char *tim_compat;
switch (device_type) {
case DDR_TYPE_LPDDR2_S2:
case DDR_TYPE_LPDDR2_S4:
tim_compat = "jedec,lpddr2-timings";
break;
default:
dev_warn(dev, "%s: un-supported memory type\n", __func__);
}
for_each_child_of_node(np_ddr, np_tim)
if (of_device_is_compatible(np_tim, tim_compat))
arr_sz++;
if (arr_sz)
timings = devm_kzalloc(dev, sizeof(*timings) * arr_sz,
GFP_KERNEL);
if (!timings)
goto default_timings;
for_each_child_of_node(np_ddr, np_tim) {
if (of_device_is_compatible(np_tim, tim_compat)) {
if (of_do_get_timings(np_tim, &timings[i])) {
devm_kfree(dev, timings);
goto default_timings;
}
i++;
}
}
*nr_frequencies = arr_sz;
return timings;
default_timings:
dev_warn(dev, "%s: using default timings\n", __func__);
*nr_frequencies = ARRAY_SIZE(lpddr2_jedec_timings);
return lpddr2_jedec_timings;
}
EXPORT_SYMBOL(of_get_ddr_timings);
/*
* OpenFirmware helpers for memory drivers
*
* Copyright (C) 2012 Texas Instruments, Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#ifndef __LINUX_MEMORY_OF_REG_H
#define __LINUX_MEMORY_OF_REG_H
#if defined(CONFIG_OF) && defined(CONFIG_DDR)
extern const struct lpddr2_min_tck *of_get_min_tck(struct device_node *np,
struct device *dev);
extern const struct lpddr2_timings
*of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
u32 device_type, u32 *nr_frequencies);
#else
static inline const struct lpddr2_min_tck
*of_get_min_tck(struct device_node *np, struct device *dev)
{
return NULL;
}
static inline const struct lpddr2_timings
*of_get_ddr_timings(struct device_node *np_ddr, struct device *dev,
u32 device_type, u32 *nr_frequencies)
{
return NULL;
}
#endif /* CONFIG_OF && CONFIG_DDR */
#endif /* __LINUX_MEMORY_OF_REG_ */
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
......@@ -113,7 +113,7 @@ int sysfs_create_link(struct kobject *kobj, struct kobject *target,
* @target: object we're pointing to.
* @name: name of the symlink.
*
* This function does the same as sysf_create_link(), but it
* This function does the same as sysfs_create_link(), but it
* doesn't warn if the link already exists.
*/
int sysfs_create_link_nowarn(struct kobject *kobj, struct kobject *target,
......
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册