Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openanolis
cloud-kernel
提交
465b2c4a
cloud-kernel
项目概览
openanolis
/
cloud-kernel
大约 2 年 前同步成功
通知
173
Star
36
Fork
7
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
10
列表
看板
标记
里程碑
合并请求
2
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
cloud-kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
10
Issue
10
列表
看板
标记
里程碑
合并请求
2
合并请求
2
Pages
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
提交
Issue看板
提交
465b2c4a
编写于
2月 10, 2017
作者:
W
Wolfram Sang
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'i2c-mux/for-next' of
https://github.com/peda-r/i2c-mux
into i2c/for-4.11
上级
71ccea09
f2114795
变更
2
隐藏空白更改
内联
并排
Showing
2 changed file
with
159 addition
and
5 deletion
+159
-5
Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt
Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt
+13
-1
drivers/i2c/muxes/i2c-mux-pca954x.c
drivers/i2c/muxes/i2c-mux-pca954x.c
+146
-4
未找到文件。
Documentation/devicetree/bindings/i2c/i2c-mux-pca954x.txt
浏览文件 @
465b2c4a
...
...
@@ -19,7 +19,14 @@ Optional Properties:
- i2c-mux-idle-disconnect: Boolean; if defined, forces mux to disconnect all
children in idle state. This is necessary for example, if there are several
multiplexers on the bus and the devices behind them use same I2C addresses.
- interrupt-parent: Phandle for the interrupt controller that services
interrupts for this device.
- interrupts: Interrupt mapping for IRQ.
- interrupt-controller: Marks the device node as an interrupt controller.
- #interrupt-cells : Should be two.
- first cell is the pin number
- second cell is used to specify flags.
See also Documentation/devicetree/bindings/interrupt-controller/interrupts.txt
Example:
...
...
@@ -29,6 +36,11 @@ Example:
#size-cells = <0>;
reg = <0x74>;
interrupt-parent = <&ipic>;
interrupts = <17 IRQ_TYPE_LEVEL_LOW>;
interrupt-controller;
#interrupt-cells = <2>;
i2c@2 {
#address-cells = <1>;
#size-cells = <0>;
...
...
drivers/i2c/muxes/i2c-mux-pca954x.c
浏览文件 @
465b2c4a
...
...
@@ -41,14 +41,20 @@
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/i2c/pca954x.h>
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/of_irq.h>
#include <linux/pm.h>
#include <linux/slab.h>
#include <linux/spinlock.h>
#define PCA954X_MAX_NCHANS 8
#define PCA954X_IRQ_OFFSET 4
enum
pca_type
{
pca_9540
,
pca_9542
,
...
...
@@ -63,6 +69,7 @@ enum pca_type {
struct
chip_desc
{
u8
nchans
;
u8
enable
;
/* used for muxes only */
u8
has_irq
;
enum
muxtype
{
pca954x_ismux
=
0
,
pca954x_isswi
...
...
@@ -75,6 +82,10 @@ struct pca954x {
u8
last_chan
;
/* last register value */
u8
deselect
;
struct
i2c_client
*
client
;
struct
irq_domain
*
irq
;
unsigned
int
irq_mask
;
spinlock_t
lock
;
};
/* Provide specs for the PCA954x types we know about */
...
...
@@ -84,17 +95,26 @@ static const struct chip_desc chips[] = {
.
enable
=
0x4
,
.
muxtype
=
pca954x_ismux
,
},
[
pca_9542
]
=
{
.
nchans
=
2
,
.
enable
=
0x4
,
.
has_irq
=
1
,
.
muxtype
=
pca954x_ismux
,
},
[
pca_9543
]
=
{
.
nchans
=
2
,
.
has_irq
=
1
,
.
muxtype
=
pca954x_isswi
,
},
[
pca_9544
]
=
{
.
nchans
=
4
,
.
enable
=
0x4
,
.
has_irq
=
1
,
.
muxtype
=
pca954x_ismux
,
},
[
pca_9545
]
=
{
.
nchans
=
4
,
.
has_irq
=
1
,
.
muxtype
=
pca954x_isswi
,
},
[
pca_9547
]
=
{
...
...
@@ -110,7 +130,7 @@ static const struct chip_desc chips[] = {
static
const
struct
i2c_device_id
pca954x_id
[]
=
{
{
"pca9540"
,
pca_9540
},
{
"pca9542"
,
pca_954
0
},
{
"pca9542"
,
pca_954
2
},
{
"pca9543"
,
pca_9543
},
{
"pca9544"
,
pca_9544
},
{
"pca9545"
,
pca_9545
},
...
...
@@ -124,7 +144,7 @@ MODULE_DEVICE_TABLE(i2c, pca954x_id);
#ifdef CONFIG_ACPI
static
const
struct
acpi_device_id
pca954x_acpi_ids
[]
=
{
{
.
id
=
"PCA9540"
,
.
driver_data
=
pca_9540
},
{
.
id
=
"PCA9542"
,
.
driver_data
=
pca_954
0
},
{
.
id
=
"PCA9542"
,
.
driver_data
=
pca_954
2
},
{
.
id
=
"PCA9543"
,
.
driver_data
=
pca_9543
},
{
.
id
=
"PCA9544"
,
.
driver_data
=
pca_9544
},
{
.
id
=
"PCA9545"
,
.
driver_data
=
pca_9545
},
...
...
@@ -218,6 +238,114 @@ static int pca954x_deselect_mux(struct i2c_mux_core *muxc, u32 chan)
return
pca954x_reg_write
(
muxc
->
parent
,
client
,
data
->
last_chan
);
}
static
irqreturn_t
pca954x_irq_handler
(
int
irq
,
void
*
dev_id
)
{
struct
pca954x
*
data
=
dev_id
;
unsigned
int
child_irq
;
int
ret
,
i
,
handled
=
0
;
ret
=
i2c_smbus_read_byte
(
data
->
client
);
if
(
ret
<
0
)
return
IRQ_NONE
;
for
(
i
=
0
;
i
<
data
->
chip
->
nchans
;
i
++
)
{
if
(
ret
&
BIT
(
PCA954X_IRQ_OFFSET
+
i
))
{
child_irq
=
irq_linear_revmap
(
data
->
irq
,
i
);
handle_nested_irq
(
child_irq
);
handled
++
;
}
}
return
handled
?
IRQ_HANDLED
:
IRQ_NONE
;
}
static
void
pca954x_irq_mask
(
struct
irq_data
*
idata
)
{
struct
pca954x
*
data
=
irq_data_get_irq_chip_data
(
idata
);
unsigned
int
pos
=
idata
->
hwirq
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
data
->
lock
,
flags
);
data
->
irq_mask
&=
~
BIT
(
pos
);
if
(
!
data
->
irq_mask
)
disable_irq
(
data
->
client
->
irq
);
spin_unlock_irqrestore
(
&
data
->
lock
,
flags
);
}
static
void
pca954x_irq_unmask
(
struct
irq_data
*
idata
)
{
struct
pca954x
*
data
=
irq_data_get_irq_chip_data
(
idata
);
unsigned
int
pos
=
idata
->
hwirq
;
unsigned
long
flags
;
spin_lock_irqsave
(
&
data
->
lock
,
flags
);
if
(
!
data
->
irq_mask
)
enable_irq
(
data
->
client
->
irq
);
data
->
irq_mask
|=
BIT
(
pos
);
spin_unlock_irqrestore
(
&
data
->
lock
,
flags
);
}
static
int
pca954x_irq_set_type
(
struct
irq_data
*
idata
,
unsigned
int
type
)
{
if
((
type
&
IRQ_TYPE_SENSE_MASK
)
!=
IRQ_TYPE_LEVEL_LOW
)
return
-
EINVAL
;
return
0
;
}
static
struct
irq_chip
pca954x_irq_chip
=
{
.
name
=
"i2c-mux-pca954x"
,
.
irq_mask
=
pca954x_irq_mask
,
.
irq_unmask
=
pca954x_irq_unmask
,
.
irq_set_type
=
pca954x_irq_set_type
,
};
static
int
pca954x_irq_setup
(
struct
i2c_mux_core
*
muxc
)
{
struct
pca954x
*
data
=
i2c_mux_priv
(
muxc
);
struct
i2c_client
*
client
=
data
->
client
;
int
c
,
err
,
irq
;
if
(
!
data
->
chip
->
has_irq
||
client
->
irq
<=
0
)
return
0
;
spin_lock_init
(
&
data
->
lock
);
data
->
irq
=
irq_domain_add_linear
(
client
->
dev
.
of_node
,
data
->
chip
->
nchans
,
&
irq_domain_simple_ops
,
data
);
if
(
!
data
->
irq
)
return
-
ENODEV
;
for
(
c
=
0
;
c
<
data
->
chip
->
nchans
;
c
++
)
{
irq
=
irq_create_mapping
(
data
->
irq
,
c
);
irq_set_chip_data
(
irq
,
data
);
irq_set_chip_and_handler
(
irq
,
&
pca954x_irq_chip
,
handle_simple_irq
);
}
err
=
devm_request_threaded_irq
(
&
client
->
dev
,
data
->
client
->
irq
,
NULL
,
pca954x_irq_handler
,
IRQF_ONESHOT
|
IRQF_SHARED
,
"pca954x"
,
data
);
if
(
err
)
goto
err_req_irq
;
disable_irq
(
data
->
client
->
irq
);
return
0
;
err_req_irq:
for
(
c
=
0
;
c
<
data
->
chip
->
nchans
;
c
++
)
{
irq
=
irq_find_mapping
(
data
->
irq
,
c
);
irq_dispose_mapping
(
irq
);
}
irq_domain_remove
(
data
->
irq
);
return
err
;
}
/*
* I2C init/probing/exit functions
*/
...
...
@@ -282,6 +410,10 @@ static int pca954x_probe(struct i2c_client *client,
idle_disconnect_dt
=
of_node
&&
of_property_read_bool
(
of_node
,
"i2c-mux-idle-disconnect"
);
ret
=
pca954x_irq_setup
(
muxc
);
if
(
ret
)
goto
fail_del_adapters
;
/* Now create an adapter for each channel */
for
(
num
=
0
;
num
<
data
->
chip
->
nchans
;
num
++
)
{
bool
idle_disconnect_pd
=
false
;
...
...
@@ -307,7 +439,7 @@ static int pca954x_probe(struct i2c_client *client,
dev_err
(
&
client
->
dev
,
"failed to register multiplexed adapter"
" %d as bus %d
\n
"
,
num
,
force
);
goto
virt_reg_failed
;
goto
fail_del_adapters
;
}
}
...
...
@@ -318,7 +450,7 @@ static int pca954x_probe(struct i2c_client *client,
return
0
;
virt_reg_failed
:
fail_del_adapters
:
i2c_mux_del_adapters
(
muxc
);
return
ret
;
}
...
...
@@ -326,6 +458,16 @@ static int pca954x_probe(struct i2c_client *client,
static
int
pca954x_remove
(
struct
i2c_client
*
client
)
{
struct
i2c_mux_core
*
muxc
=
i2c_get_clientdata
(
client
);
struct
pca954x
*
data
=
i2c_mux_priv
(
muxc
);
int
c
,
irq
;
if
(
data
->
irq
)
{
for
(
c
=
0
;
c
<
data
->
chip
->
nchans
;
c
++
)
{
irq
=
irq_find_mapping
(
data
->
irq
,
c
);
irq_dispose_mapping
(
irq
);
}
irq_domain_remove
(
data
->
irq
);
}
i2c_mux_del_adapters
(
muxc
);
return
0
;
...
...
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录