Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
openeuler
Kernel
提交
f6d29536
K
Kernel
项目概览
openeuler
/
Kernel
接近 2 年 前同步成功
通知
8
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
Kernel
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
f6d29536
编写于
11月 01, 2017
作者:
W
Wolfram Sang
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'i2c/sbs-manager' into i2c/for-4.15
上级
4ee045f4
4cf419a2
变更
14
隐藏空白更改
内联
并排
Showing
14 changed file
with
675 addition
and
148 deletion
+675
-148
Documentation/devicetree/bindings/i2c/i2c.txt
Documentation/devicetree/bindings/i2c/i2c.txt
+2
-2
Documentation/devicetree/bindings/power/supply/sbs,sbs-manager.txt
...tion/devicetree/bindings/power/supply/sbs,sbs-manager.txt
+66
-0
drivers/i2c/busses/i2c-parport-light.c
drivers/i2c/busses/i2c-parport-light.c
+0
-1
drivers/i2c/busses/i2c-parport.c
drivers/i2c/busses/i2c-parport.c
+0
-1
drivers/i2c/busses/i2c-thunderx-pcidrv.c
drivers/i2c/busses/i2c-thunderx-pcidrv.c
+0
-6
drivers/i2c/i2c-core-base.c
drivers/i2c/i2c-core-base.c
+9
-0
drivers/i2c/i2c-core-smbus.c
drivers/i2c/i2c-core-smbus.c
+55
-0
drivers/i2c/i2c-smbus.c
drivers/i2c/i2c-smbus.c
+25
-56
drivers/i2c/muxes/i2c-mux-pca954x.c
drivers/i2c/muxes/i2c-mux-pca954x.c
+32
-63
drivers/power/supply/Kconfig
drivers/power/supply/Kconfig
+14
-0
drivers/power/supply/Makefile
drivers/power/supply/Makefile
+1
-0
drivers/power/supply/sbs-battery.c
drivers/power/supply/sbs-battery.c
+17
-18
drivers/power/supply/sbs-manager.c
drivers/power/supply/sbs-manager.c
+445
-0
include/linux/i2c-smbus.h
include/linux/i2c-smbus.h
+9
-1
未找到文件。
Documentation/devicetree/bindings/i2c/i2c.txt
浏览文件 @
f6d29536
...
...
@@ -59,8 +59,8 @@ wants to support one of the below features, it should adapt the bindings below.
interrupts used by the device.
- interrupt-names
"irq"
and "wakeup" names are recognized by I2C core, other names are
left to individual drivers.
"irq"
, "wakeup" and "smbus_alert" names are recognized by I2C core,
other names are
left to individual drivers.
- host-notify
device uses SMBus host notify protocol instead of interrupt line.
...
...
Documentation/devicetree/bindings/power/supply/sbs,sbs-manager.txt
0 → 100644
浏览文件 @
f6d29536
Binding for sbs-manager
Required properties:
- compatible: "<vendor>,<part-number>", "sbs,sbs-charger" as fallback. The part
number compatible string might be used in order to take care of vendor
specific registers.
- reg: integer, i2c address of the device. Should be <0xa>.
Optional properties:
- gpio-controller: Marks the port as GPIO controller.
See "gpio-specifier" in .../devicetree/bindings/gpio/gpio.txt.
- #gpio-cells: Should be <2>. The first cell is the pin number, the second cell
is used to specify optional parameters:
See "gpio-specifier" in .../devicetree/bindings/gpio/gpio.txt.
From OS view the device is basically an i2c-mux used to communicate with up to
four smart battery devices at address 0xb. The driver actually implements this
behaviour. So standard i2c-mux nodes can be used to register up to four slave
batteries. Channels will be numerated starting from 1 to 4.
Example:
batman@a {
compatible = "lltc,ltc1760", "sbs,sbs-manager";
reg = <0x0a>;
#address-cells = <1>;
#size-cells = <0>;
gpio-controller;
#gpio-cells = <2>;
i2c@1 {
#address-cells = <1>;
#size-cells = <0>;
reg = <1>;
battery@b {
compatible = "ti,bq2060", "sbs,sbs-battery";
reg = <0x0b>;
sbs,battery-detect-gpios = <&batman 1 1>;
};
};
i2c@2 {
#address-cells = <1>;
#size-cells = <0>;
reg = <2>;
battery@b {
compatible = "ti,bq2060", "sbs,sbs-battery";
reg = <0x0b>;
sbs,battery-detect-gpios = <&batman 2 1>;
};
};
i2c@3 {
#address-cells = <1>;
#size-cells = <0>;
reg = <3>;
battery@b {
compatible = "ti,bq2060", "sbs,sbs-battery";
reg = <0x0b>;
sbs,battery-detect-gpios = <&batman 3 1>;
};
};
};
drivers/i2c/busses/i2c-parport-light.c
浏览文件 @
f6d29536
...
...
@@ -123,7 +123,6 @@ static struct i2c_adapter parport_adapter = {
/* SMBus alert support */
static
struct
i2c_smbus_alert_setup
alert_data
=
{
.
alert_edge_triggered
=
1
,
};
static
struct
i2c_client
*
ara
;
static
struct
lineop
parport_ctrl_irq
=
{
...
...
drivers/i2c/busses/i2c-parport.c
浏览文件 @
f6d29536
...
...
@@ -237,7 +237,6 @@ static void i2c_parport_attach(struct parport *port)
/* Setup SMBus alert if supported */
if
(
adapter_parm
[
type
].
smbus_alert
)
{
adapter
->
alert_data
.
alert_edge_triggered
=
1
;
adapter
->
ara
=
i2c_setup_smbus_alert
(
&
adapter
->
adapter
,
&
adapter
->
alert_data
);
if
(
adapter
->
ara
)
...
...
drivers/i2c/busses/i2c-thunderx-pcidrv.c
浏览文件 @
f6d29536
...
...
@@ -118,8 +118,6 @@ static void thunder_i2c_clock_disable(struct device *dev, struct clk *clk)
static
int
thunder_i2c_smbus_setup_of
(
struct
octeon_i2c
*
i2c
,
struct
device_node
*
node
)
{
u32
type
;
if
(
!
node
)
return
-
EINVAL
;
...
...
@@ -127,10 +125,6 @@ static int thunder_i2c_smbus_setup_of(struct octeon_i2c *i2c,
if
(
!
i2c
->
alert_data
.
irq
)
return
-
EINVAL
;
type
=
irqd_get_trigger_type
(
irq_get_irq_data
(
i2c
->
alert_data
.
irq
));
i2c
->
alert_data
.
alert_edge_triggered
=
(
type
&
IRQ_TYPE_LEVEL_MASK
)
?
1
:
0
;
i2c
->
ara
=
i2c_setup_smbus_alert
(
&
i2c
->
adap
,
&
i2c
->
alert_data
);
if
(
!
i2c
->
ara
)
return
-
ENODEV
;
...
...
drivers/i2c/i2c-core-base.c
浏览文件 @
f6d29536
...
...
@@ -29,6 +29,7 @@
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/i2c.h>
#include <linux/i2c-smbus.h>
#include <linux/idr.h>
#include <linux/init.h>
#include <linux/irqflags.h>
...
...
@@ -1269,6 +1270,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
goto
out_list
;
}
res
=
of_i2c_setup_smbus_alert
(
adap
);
if
(
res
)
goto
out_reg
;
dev_dbg
(
&
adap
->
dev
,
"adapter [%s] registered
\n
"
,
adap
->
name
);
pm_runtime_no_callbacks
(
&
adap
->
dev
);
...
...
@@ -1300,6 +1305,10 @@ static int i2c_register_adapter(struct i2c_adapter *adap)
return
0
;
out_reg:
init_completion
(
&
adap
->
dev_released
);
device_unregister
(
&
adap
->
dev
);
wait_for_completion
(
&
adap
->
dev_released
);
out_list:
mutex_lock
(
&
core_lock
);
idr_remove
(
&
i2c_adapter_idr
,
adap
->
nr
);
...
...
drivers/i2c/i2c-core-smbus.c
浏览文件 @
f6d29536
...
...
@@ -17,6 +17,7 @@
#include <linux/device.h>
#include <linux/err.h>
#include <linux/i2c.h>
#include <linux/i2c-smbus.h>
#define CREATE_TRACE_POINTS
#include <trace/events/smbus.h>
...
...
@@ -592,3 +593,57 @@ s32 i2c_smbus_read_i2c_block_data_or_emulated(const struct i2c_client *client,
return
i
;
}
EXPORT_SYMBOL
(
i2c_smbus_read_i2c_block_data_or_emulated
);
/**
* i2c_setup_smbus_alert - Setup SMBus alert support
* @adapter: the target adapter
* @setup: setup data for the SMBus alert handler
* Context: can sleep
*
* Setup handling of the SMBus alert protocol on a given I2C bus segment.
*
* Handling can be done either through our IRQ handler, or by the
* adapter (from its handler, periodic polling, or whatever).
*
* NOTE that if we manage the IRQ, we *MUST* know if it's level or
* edge triggered in order to hand it to the workqueue correctly.
* If triggering the alert seems to wedge the system, you probably
* should have said it's level triggered.
*
* This returns the ara client, which should be saved for later use with
* i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or NULL
* to indicate an error.
*/
struct
i2c_client
*
i2c_setup_smbus_alert
(
struct
i2c_adapter
*
adapter
,
struct
i2c_smbus_alert_setup
*
setup
)
{
struct
i2c_board_info
ara_board_info
=
{
I2C_BOARD_INFO
(
"smbus_alert"
,
0x0c
),
.
platform_data
=
setup
,
};
return
i2c_new_device
(
adapter
,
&
ara_board_info
);
}
EXPORT_SYMBOL_GPL
(
i2c_setup_smbus_alert
);
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF)
int
of_i2c_setup_smbus_alert
(
struct
i2c_adapter
*
adapter
)
{
struct
i2c_client
*
client
;
int
irq
;
irq
=
of_property_match_string
(
adapter
->
dev
.
of_node
,
"interrupt-names"
,
"smbus_alert"
);
if
(
irq
==
-
EINVAL
||
irq
==
-
ENODATA
)
return
0
;
else
if
(
irq
<
0
)
return
irq
;
client
=
i2c_setup_smbus_alert
(
adapter
,
NULL
);
if
(
!
client
)
return
-
ENODEV
;
return
0
;
}
EXPORT_SYMBOL_GPL
(
of_i2c_setup_smbus_alert
);
#endif
drivers/i2c/i2c-smbus.c
浏览文件 @
f6d29536
...
...
@@ -21,12 +21,11 @@
#include <linux/interrupt.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/of_irq.h>
#include <linux/slab.h>
#include <linux/workqueue.h>
struct
i2c_smbus_alert
{
unsigned
int
alert_edge_triggered
:
1
;
int
irq
;
struct
work_struct
alert
;
struct
i2c_client
*
ara
;
/* Alert response address */
};
...
...
@@ -72,13 +71,12 @@ static int smbus_do_alert(struct device *dev, void *addrp)
* The alert IRQ handler needs to hand work off to a task which can issue
* SMBus calls, because those sleeping calls can't be made in IRQ context.
*/
static
void
smbus_alert
(
struct
work_struct
*
work
)
static
irqreturn_t
smbus_alert
(
int
irq
,
void
*
d
)
{
struct
i2c_smbus_alert
*
alert
;
struct
i2c_smbus_alert
*
alert
=
d
;
struct
i2c_client
*
ara
;
unsigned
short
prev_addr
=
0
;
/* Not a valid address */
alert
=
container_of
(
work
,
struct
i2c_smbus_alert
,
alert
);
ara
=
alert
->
ara
;
for
(;;)
{
...
...
@@ -115,21 +113,17 @@ static void smbus_alert(struct work_struct *work)
prev_addr
=
data
.
addr
;
}
/* We handled all alerts; re-enable level-triggered IRQs */
if
(
!
alert
->
alert_edge_triggered
)
enable_irq
(
alert
->
irq
);
return
IRQ_HANDLED
;
}
static
irqreturn_t
smbalert_irq
(
int
irq
,
void
*
d
)
static
void
smbalert_work
(
struct
work_struct
*
work
)
{
struct
i2c_smbus_alert
*
alert
=
d
;
struct
i2c_smbus_alert
*
alert
;
/* Disable level-triggered IRQs until we handle them */
if
(
!
alert
->
alert_edge_triggered
)
disable_irq_nosync
(
irq
);
alert
=
container_of
(
work
,
struct
i2c_smbus_alert
,
alert
);
smbus_alert
(
0
,
alert
);
schedule_work
(
&
alert
->
alert
);
return
IRQ_HANDLED
;
}
/* Setup SMBALERT# infrastructure */
...
...
@@ -139,28 +133,35 @@ static int smbalert_probe(struct i2c_client *ara,
struct
i2c_smbus_alert_setup
*
setup
=
dev_get_platdata
(
&
ara
->
dev
);
struct
i2c_smbus_alert
*
alert
;
struct
i2c_adapter
*
adapter
=
ara
->
adapter
;
int
res
;
int
res
,
irq
;
alert
=
devm_kzalloc
(
&
ara
->
dev
,
sizeof
(
struct
i2c_smbus_alert
),
GFP_KERNEL
);
if
(
!
alert
)
return
-
ENOMEM
;
alert
->
alert_edge_triggered
=
setup
->
alert_edge_triggered
;
alert
->
irq
=
setup
->
irq
;
INIT_WORK
(
&
alert
->
alert
,
smbus_alert
);
if
(
setup
)
{
irq
=
setup
->
irq
;
}
else
{
irq
=
of_irq_get_byname
(
adapter
->
dev
.
of_node
,
"smbus_alert"
);
if
(
irq
<=
0
)
return
irq
;
}
INIT_WORK
(
&
alert
->
alert
,
smbalert_work
);
alert
->
ara
=
ara
;
if
(
setup
->
irq
>
0
)
{
res
=
devm_request_irq
(
&
ara
->
dev
,
setup
->
irq
,
smbalert_irq
,
0
,
"smbus_alert"
,
alert
);
if
(
irq
>
0
)
{
res
=
devm_request_threaded_irq
(
&
ara
->
dev
,
irq
,
NULL
,
smbus_alert
,
IRQF_SHARED
|
IRQF_ONESHOT
,
"smbus_alert"
,
alert
);
if
(
res
)
return
res
;
}
i2c_set_clientdata
(
ara
,
alert
);
dev_info
(
&
adapter
->
dev
,
"supports SMBALERT#, %s trigger
\n
"
,
setup
->
alert_edge_triggered
?
"edge"
:
"level"
);
dev_info
(
&
adapter
->
dev
,
"supports SMBALERT#
\n
"
);
return
0
;
}
...
...
@@ -189,38 +190,6 @@ static struct i2c_driver smbalert_driver = {
.
id_table
=
smbalert_ids
,
};
/**
* i2c_setup_smbus_alert - Setup SMBus alert support
* @adapter: the target adapter
* @setup: setup data for the SMBus alert handler
* Context: can sleep
*
* Setup handling of the SMBus alert protocol on a given I2C bus segment.
*
* Handling can be done either through our IRQ handler, or by the
* adapter (from its handler, periodic polling, or whatever).
*
* NOTE that if we manage the IRQ, we *MUST* know if it's level or
* edge triggered in order to hand it to the workqueue correctly.
* If triggering the alert seems to wedge the system, you probably
* should have said it's level triggered.
*
* This returns the ara client, which should be saved for later use with
* i2c_handle_smbus_alert() and ultimately i2c_unregister_device(); or NULL
* to indicate an error.
*/
struct
i2c_client
*
i2c_setup_smbus_alert
(
struct
i2c_adapter
*
adapter
,
struct
i2c_smbus_alert_setup
*
setup
)
{
struct
i2c_board_info
ara_board_info
=
{
I2C_BOARD_INFO
(
"smbus_alert"
,
0x0c
),
.
platform_data
=
setup
,
};
return
i2c_new_device
(
adapter
,
&
ara_board_info
);
}
EXPORT_SYMBOL_GPL
(
i2c_setup_smbus_alert
);
/**
* i2c_handle_smbus_alert - Handle an SMBus alert
* @ara: the ARA client on the relevant adapter
...
...
drivers/i2c/muxes/i2c-mux-pca954x.c
浏览文件 @
f6d29536
...
...
@@ -246,36 +246,6 @@ static irqreturn_t pca954x_irq_handler(int irq, void *dev_id)
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
;
raw_spin_lock_irqsave
(
&
data
->
lock
,
flags
);
data
->
irq_mask
&=
~
BIT
(
pos
);
if
(
!
data
->
irq_mask
)
disable_irq
(
data
->
client
->
irq
);
raw_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
;
raw_spin_lock_irqsave
(
&
data
->
lock
,
flags
);
if
(
!
data
->
irq_mask
)
enable_irq
(
data
->
client
->
irq
);
data
->
irq_mask
|=
BIT
(
pos
);
raw_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
)
...
...
@@ -285,8 +255,6 @@ static int pca954x_irq_set_type(struct irq_data *idata, unsigned int type)
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
,
};
...
...
@@ -294,7 +262,7 @@ 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
;
int
c
,
irq
;
if
(
!
data
->
chip
->
has_irq
||
client
->
irq
<=
0
)
return
0
;
...
...
@@ -309,29 +277,31 @@ static int pca954x_irq_setup(struct i2c_mux_core *muxc)
for
(
c
=
0
;
c
<
data
->
chip
->
nchans
;
c
++
)
{
irq
=
irq_create_mapping
(
data
->
irq
,
c
);
if
(
!
irq
)
{
dev_err
(
&
client
->
dev
,
"failed irq create map
\n
"
);
return
-
EINVAL
;
}
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
;
return
0
;
}
disable_irq
(
data
->
client
->
irq
);
static
void
pca954x_cleanup
(
struct
i2c_mux_core
*
muxc
)
{
struct
pca954x
*
data
=
i2c_mux_priv
(
muxc
);
int
c
,
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
);
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
);
}
irq_domain_remove
(
data
->
irq
);
return
err
;
i2c_mux_del_adapters
(
muxc
);
}
/*
...
...
@@ -391,7 +361,7 @@ static int pca954x_probe(struct i2c_client *client,
ret
=
pca954x_irq_setup
(
muxc
);
if
(
ret
)
goto
fail_
del_adapters
;
goto
fail_
cleanup
;
/* Now create an adapter for each channel */
for
(
num
=
0
;
num
<
data
->
chip
->
nchans
;
num
++
)
{
...
...
@@ -414,7 +384,16 @@ static int pca954x_probe(struct i2c_client *client,
ret
=
i2c_mux_add_adapter
(
muxc
,
force
,
num
,
class
);
if
(
ret
)
goto
fail_del_adapters
;
goto
fail_cleanup
;
}
if
(
data
->
irq
)
{
ret
=
devm_request_threaded_irq
(
&
client
->
dev
,
data
->
client
->
irq
,
NULL
,
pca954x_irq_handler
,
IRQF_ONESHOT
|
IRQF_SHARED
,
"pca954x"
,
data
);
if
(
ret
)
goto
fail_cleanup
;
}
dev_info
(
&
client
->
dev
,
...
...
@@ -424,26 +403,16 @@ static int pca954x_probe(struct i2c_client *client,
return
0
;
fail_
del_adapters
:
i2c_mux_del_adapters
(
muxc
);
fail_
cleanup
:
pca954x_cleanup
(
muxc
);
return
ret
;
}
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
);
pca954x_cleanup
(
muxc
);
return
0
;
}
...
...
drivers/power/supply/Kconfig
浏览文件 @
f6d29536
...
...
@@ -184,6 +184,20 @@ config CHARGER_SBS
help
Say Y to include support for SBS compilant battery chargers.
config MANAGER_SBS
tristate "Smart Battery System Manager"
depends on I2C && I2C_MUX && GPIOLIB
select I2C_SMBUS
help
Say Y here to include support for Smart Battery System Manager
ICs. The driver reports online and charging status via sysfs.
It presents itself also as I2C mux which allows to bind
smart battery driver to its ports.
Supported is for example LTC1760.
This driver can also be built as a module. If so, the module will be
called sbs-manager.
config BATTERY_BQ27XXX
tristate "BQ27xxx battery driver"
help
...
...
drivers/power/supply/Makefile
浏览文件 @
f6d29536
...
...
@@ -36,6 +36,7 @@ obj-$(CONFIG_BATTERY_IPAQ_MICRO) += ipaq_micro_battery.o
obj-$(CONFIG_BATTERY_WM97XX)
+=
wm97xx_battery.o
obj-$(CONFIG_BATTERY_SBS)
+=
sbs-battery.o
obj-$(CONFIG_CHARGER_SBS)
+=
sbs-charger.o
obj-$(CONFIG_MANAGER_SBS)
+=
sbs-manager.o
obj-$(CONFIG_BATTERY_BQ27XXX)
+=
bq27xxx_battery.o
obj-$(CONFIG_BATTERY_BQ27XXX_I2C)
+=
bq27xxx_battery_i2c.o
obj-$(CONFIG_BATTERY_BQ27XXX_HDQ)
+=
bq27xxx_battery_hdq.o
...
...
drivers/power/supply/sbs-battery.c
浏览文件 @
f6d29536
...
...
@@ -177,10 +177,8 @@ static bool force_load;
static
int
sbs_read_word_data
(
struct
i2c_client
*
client
,
u8
address
)
{
struct
sbs_info
*
chip
=
i2c_get_clientdata
(
client
);
int
retries
=
chip
->
i2c_retry_count
;
s32
ret
=
0
;
int
retries
=
1
;
retries
=
chip
->
i2c_retry_count
;
while
(
retries
>
0
)
{
ret
=
i2c_smbus_read_word_data
(
client
,
address
);
...
...
@@ -204,7 +202,7 @@ static int sbs_read_string_data(struct i2c_client *client, u8 address,
{
struct
sbs_info
*
chip
=
i2c_get_clientdata
(
client
);
s32
ret
=
0
,
block_length
=
0
;
int
retries_length
=
1
,
retries_block
=
1
;
int
retries_length
,
retries_block
;
u8
block_buffer
[
I2C_SMBUS_BLOCK_MAX
+
1
];
retries_length
=
chip
->
i2c_retry_count
;
...
...
@@ -269,10 +267,8 @@ static int sbs_write_word_data(struct i2c_client *client, u8 address,
u16
value
)
{
struct
sbs_info
*
chip
=
i2c_get_clientdata
(
client
);
int
retries
=
chip
->
i2c_retry_count
;
s32
ret
=
0
;
int
retries
=
1
;
retries
=
chip
->
i2c_retry_count
;
while
(
retries
>
0
)
{
ret
=
i2c_smbus_write_word_data
(
client
,
address
,
value
);
...
...
@@ -321,16 +317,6 @@ static int sbs_get_battery_presence_and_health(
union
power_supply_propval
*
val
)
{
s32
ret
;
struct
sbs_info
*
chip
=
i2c_get_clientdata
(
client
);
if
(
psp
==
POWER_SUPPLY_PROP_PRESENT
&&
chip
->
gpio_detect
)
{
ret
=
gpiod_get_value_cansleep
(
chip
->
gpio_detect
);
if
(
ret
<
0
)
return
ret
;
val
->
intval
=
ret
;
chip
->
is_present
=
val
->
intval
;
return
ret
;
}
/*
* Write to ManufacturerAccess with ManufacturerAccess command
...
...
@@ -570,7 +556,7 @@ static int sbs_get_battery_serial_number(struct i2c_client *client,
if
(
ret
<
0
)
return
ret
;
ret
=
sprintf
(
sbs_serial
,
"%04x"
,
ret
);
sprintf
(
sbs_serial
,
"%04x"
,
ret
);
val
->
strval
=
sbs_serial
;
return
0
;
...
...
@@ -598,6 +584,19 @@ static int sbs_get_property(struct power_supply *psy,
struct
sbs_info
*
chip
=
power_supply_get_drvdata
(
psy
);
struct
i2c_client
*
client
=
chip
->
client
;
if
(
chip
->
gpio_detect
)
{
ret
=
gpiod_get_value_cansleep
(
chip
->
gpio_detect
);
if
(
ret
<
0
)
return
ret
;
if
(
psp
==
POWER_SUPPLY_PROP_PRESENT
)
{
val
->
intval
=
ret
;
chip
->
is_present
=
val
->
intval
;
return
0
;
}
if
(
ret
==
0
)
return
-
ENODATA
;
}
switch
(
psp
)
{
case
POWER_SUPPLY_PROP_PRESENT
:
case
POWER_SUPPLY_PROP_HEALTH
:
...
...
drivers/power/supply/sbs-manager.c
0 → 100644
浏览文件 @
f6d29536
/*
* Driver for SBS compliant Smart Battery System Managers
*
* The device communicates via i2c at address 0x0a and multiplexes access to up
* to four smart batteries at address 0x0b.
*
* Via sysfs interface the online state and charge type are presented.
*
* Datasheet SBSM: http://sbs-forum.org/specs/sbsm100b.pdf
* Datasheet LTC1760: http://cds.linear.com/docs/en/datasheet/1760fb.pdf
*
* Karl-Heinz Schneider <karl-heinz@schneider-inet.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 as
* published by the Free Software Foundation.
*/
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/i2c-mux.h>
#include <linux/power_supply.h>
#include <linux/property.h>
#define SBSM_MAX_BATS 4
#define SBSM_RETRY_CNT 3
/* registers addresses */
#define SBSM_CMD_BATSYSSTATE 0x01
#define SBSM_CMD_BATSYSSTATECONT 0x02
#define SBSM_CMD_BATSYSINFO 0x04
#define SBSM_CMD_LTC 0x3c
#define SBSM_MASK_BAT_SUPPORTED GENMASK(3, 0)
#define SBSM_MASK_CHARGE_BAT GENMASK(7, 4)
#define SBSM_BIT_AC_PRESENT BIT(0)
#define SBSM_BIT_TURBO BIT(7)
#define SBSM_SMB_BAT_OFFSET 11
struct
sbsm_data
{
struct
i2c_client
*
client
;
struct
i2c_mux_core
*
muxc
;
struct
power_supply
*
psy
;
u8
cur_chan
;
/* currently selected channel */
struct
gpio_chip
chip
;
bool
is_ltc1760
;
/* special capabilities */
unsigned
int
supported_bats
;
unsigned
int
last_state
;
unsigned
int
last_state_cont
;
};
static
enum
power_supply_property
sbsm_props
[]
=
{
POWER_SUPPLY_PROP_ONLINE
,
POWER_SUPPLY_PROP_CHARGE_TYPE
,
};
static
int
sbsm_read_word
(
struct
i2c_client
*
client
,
u8
address
)
{
int
reg
,
retries
;
for
(
retries
=
SBSM_RETRY_CNT
;
retries
>
0
;
retries
--
)
{
reg
=
i2c_smbus_read_word_data
(
client
,
address
);
if
(
reg
>=
0
)
break
;
}
if
(
reg
<
0
)
{
dev_err
(
&
client
->
dev
,
"failed to read register 0x%02x
\n
"
,
address
);
}
return
reg
;
}
static
int
sbsm_write_word
(
struct
i2c_client
*
client
,
u8
address
,
u16
word
)
{
int
ret
,
retries
;
for
(
retries
=
SBSM_RETRY_CNT
;
retries
>
0
;
retries
--
)
{
ret
=
i2c_smbus_write_word_data
(
client
,
address
,
word
);
if
(
ret
>=
0
)
break
;
}
if
(
ret
<
0
)
dev_err
(
&
client
->
dev
,
"failed to write to register 0x%02x
\n
"
,
address
);
return
ret
;
}
static
int
sbsm_get_property
(
struct
power_supply
*
psy
,
enum
power_supply_property
psp
,
union
power_supply_propval
*
val
)
{
struct
sbsm_data
*
data
=
power_supply_get_drvdata
(
psy
);
int
regval
=
0
;
switch
(
psp
)
{
case
POWER_SUPPLY_PROP_ONLINE
:
regval
=
sbsm_read_word
(
data
->
client
,
SBSM_CMD_BATSYSSTATECONT
);
if
(
regval
<
0
)
return
regval
;
val
->
intval
=
!!
(
regval
&
SBSM_BIT_AC_PRESENT
);
break
;
case
POWER_SUPPLY_PROP_CHARGE_TYPE
:
regval
=
sbsm_read_word
(
data
->
client
,
SBSM_CMD_BATSYSSTATE
);
if
(
regval
<
0
)
return
regval
;
if
((
regval
&
SBSM_MASK_CHARGE_BAT
)
==
0
)
{
val
->
intval
=
POWER_SUPPLY_CHARGE_TYPE_NONE
;
return
0
;
}
val
->
intval
=
POWER_SUPPLY_CHARGE_TYPE_TRICKLE
;
if
(
data
->
is_ltc1760
)
{
/* charge mode fast if turbo is active */
regval
=
sbsm_read_word
(
data
->
client
,
SBSM_CMD_LTC
);
if
(
regval
<
0
)
return
regval
;
else
if
(
regval
&
SBSM_BIT_TURBO
)
val
->
intval
=
POWER_SUPPLY_CHARGE_TYPE_FAST
;
}
break
;
default:
return
-
EINVAL
;
}
return
0
;
}
static
int
sbsm_prop_is_writeable
(
struct
power_supply
*
psy
,
enum
power_supply_property
psp
)
{
struct
sbsm_data
*
data
=
power_supply_get_drvdata
(
psy
);
return
(
psp
==
POWER_SUPPLY_PROP_CHARGE_TYPE
)
&&
data
->
is_ltc1760
;
}
static
int
sbsm_set_property
(
struct
power_supply
*
psy
,
enum
power_supply_property
psp
,
const
union
power_supply_propval
*
val
)
{
struct
sbsm_data
*
data
=
power_supply_get_drvdata
(
psy
);
int
ret
=
-
EINVAL
;
u16
regval
;
switch
(
psp
)
{
case
POWER_SUPPLY_PROP_CHARGE_TYPE
:
/* write 1 to TURBO if type fast is given */
if
(
!
data
->
is_ltc1760
)
break
;
regval
=
val
->
intval
==
POWER_SUPPLY_CHARGE_TYPE_FAST
?
SBSM_BIT_TURBO
:
0
;
ret
=
sbsm_write_word
(
data
->
client
,
SBSM_CMD_LTC
,
regval
);
break
;
default:
break
;
}
return
ret
;
}
/*
* Switch to battery
* Parameter chan is directly the content of SMB_BAT* nibble
*/
static
int
sbsm_select
(
struct
i2c_mux_core
*
muxc
,
u32
chan
)
{
struct
sbsm_data
*
data
=
i2c_mux_priv
(
muxc
);
struct
device
*
dev
=
&
data
->
client
->
dev
;
int
ret
=
0
;
u16
reg
;
if
(
data
->
cur_chan
==
chan
)
return
ret
;
/* chan goes from 1 ... 4 */
reg
=
1
<<
BIT
(
SBSM_SMB_BAT_OFFSET
+
chan
);
ret
=
sbsm_write_word
(
data
->
client
,
SBSM_CMD_BATSYSSTATE
,
reg
);
if
(
ret
)
dev_err
(
dev
,
"Failed to select channel %i
\n
"
,
chan
);
else
data
->
cur_chan
=
chan
;
return
ret
;
}
static
int
sbsm_gpio_get_value
(
struct
gpio_chip
*
gc
,
unsigned
int
off
)
{
struct
sbsm_data
*
data
=
gpiochip_get_data
(
gc
);
int
ret
;
ret
=
sbsm_read_word
(
data
->
client
,
SBSM_CMD_BATSYSSTATE
);
if
(
ret
<
0
)
return
ret
;
return
ret
&
BIT
(
off
);
}
/*
* This needs to be defined or the GPIO lib fails to register the pin.
* But the 'gpio' is always an input.
*/
static
int
sbsm_gpio_direction_input
(
struct
gpio_chip
*
gc
,
unsigned
int
off
)
{
return
0
;
}
static
int
sbsm_do_alert
(
struct
device
*
dev
,
void
*
d
)
{
struct
i2c_client
*
client
=
i2c_verify_client
(
dev
);
struct
i2c_driver
*
driver
;
if
(
!
client
||
client
->
addr
!=
0x0b
)
return
0
;
device_lock
(
dev
);
if
(
client
->
dev
.
driver
)
{
driver
=
to_i2c_driver
(
client
->
dev
.
driver
);
if
(
driver
->
alert
)
driver
->
alert
(
client
,
I2C_PROTOCOL_SMBUS_ALERT
,
0
);
else
dev_warn
(
&
client
->
dev
,
"no driver alert()!
\n
"
);
}
else
{
dev_dbg
(
&
client
->
dev
,
"alert with no driver
\n
"
);
}
device_unlock
(
dev
);
return
-
EBUSY
;
}
static
void
sbsm_alert
(
struct
i2c_client
*
client
,
enum
i2c_alert_protocol
prot
,
unsigned
int
d
)
{
struct
sbsm_data
*
sbsm
=
i2c_get_clientdata
(
client
);
int
ret
,
i
,
irq_bat
=
0
,
state
=
0
;
ret
=
sbsm_read_word
(
sbsm
->
client
,
SBSM_CMD_BATSYSSTATE
);
if
(
ret
>=
0
)
{
irq_bat
=
ret
^
sbsm
->
last_state
;
sbsm
->
last_state
=
ret
;
state
=
ret
;
}
ret
=
sbsm_read_word
(
sbsm
->
client
,
SBSM_CMD_BATSYSSTATECONT
);
if
((
ret
>=
0
)
&&
((
ret
^
sbsm
->
last_state_cont
)
&
SBSM_BIT_AC_PRESENT
))
{
irq_bat
|=
sbsm
->
supported_bats
&
state
;
power_supply_changed
(
sbsm
->
psy
);
}
sbsm
->
last_state_cont
=
ret
;
for
(
i
=
0
;
i
<
SBSM_MAX_BATS
;
i
++
)
{
if
(
irq_bat
&
BIT
(
i
))
{
device_for_each_child
(
&
sbsm
->
muxc
->
adapter
[
i
]
->
dev
,
NULL
,
sbsm_do_alert
);
}
}
}
static
int
sbsm_gpio_setup
(
struct
sbsm_data
*
data
)
{
struct
gpio_chip
*
gc
=
&
data
->
chip
;
struct
i2c_client
*
client
=
data
->
client
;
struct
device
*
dev
=
&
client
->
dev
;
int
ret
;
if
(
!
device_property_present
(
dev
,
"gpio-controller"
))
return
0
;
ret
=
sbsm_read_word
(
client
,
SBSM_CMD_BATSYSSTATE
);
if
(
ret
<
0
)
return
ret
;
data
->
last_state
=
ret
;
ret
=
sbsm_read_word
(
client
,
SBSM_CMD_BATSYSSTATECONT
);
if
(
ret
<
0
)
return
ret
;
data
->
last_state_cont
=
ret
;
gc
->
get
=
sbsm_gpio_get_value
;
gc
->
direction_input
=
sbsm_gpio_direction_input
;
gc
->
can_sleep
=
true
;
gc
->
base
=
-
1
;
gc
->
ngpio
=
SBSM_MAX_BATS
;
gc
->
label
=
client
->
name
;
gc
->
parent
=
dev
;
gc
->
owner
=
THIS_MODULE
;
ret
=
devm_gpiochip_add_data
(
dev
,
gc
,
data
);
if
(
ret
)
{
dev_err
(
dev
,
"devm_gpiochip_add_data failed: %d
\n
"
,
ret
);
return
ret
;
}
return
ret
;
}
static
const
struct
power_supply_desc
sbsm_default_psy_desc
=
{
.
type
=
POWER_SUPPLY_TYPE_MAINS
,
.
properties
=
sbsm_props
,
.
num_properties
=
ARRAY_SIZE
(
sbsm_props
),
.
get_property
=
&
sbsm_get_property
,
.
set_property
=
&
sbsm_set_property
,
.
property_is_writeable
=
&
sbsm_prop_is_writeable
,
};
static
int
sbsm_probe
(
struct
i2c_client
*
client
,
const
struct
i2c_device_id
*
id
)
{
struct
i2c_adapter
*
adapter
=
to_i2c_adapter
(
client
->
dev
.
parent
);
struct
sbsm_data
*
data
;
struct
device
*
dev
=
&
client
->
dev
;
struct
power_supply_desc
*
psy_desc
;
struct
power_supply_config
psy_cfg
=
{};
int
ret
=
0
,
i
;
/* Device listens only at address 0x0a */
if
(
client
->
addr
!=
0x0a
)
return
-
EINVAL
;
if
(
!
i2c_check_functionality
(
adapter
,
I2C_FUNC_SMBUS_WORD_DATA
))
return
-
EPFNOSUPPORT
;
data
=
devm_kzalloc
(
dev
,
sizeof
(
*
data
),
GFP_KERNEL
);
if
(
!
data
)
return
-
ENOMEM
;
i2c_set_clientdata
(
client
,
data
);
data
->
client
=
client
;
data
->
is_ltc1760
=
!!
strstr
(
id
->
name
,
"ltc1760"
);
ret
=
sbsm_read_word
(
client
,
SBSM_CMD_BATSYSINFO
);
if
(
ret
<
0
)
return
ret
;
data
->
supported_bats
=
ret
&
SBSM_MASK_BAT_SUPPORTED
;
data
->
muxc
=
i2c_mux_alloc
(
adapter
,
dev
,
SBSM_MAX_BATS
,
0
,
I2C_MUX_LOCKED
,
&
sbsm_select
,
NULL
);
if
(
!
data
->
muxc
)
{
dev_err
(
dev
,
"failed to alloc i2c mux
\n
"
);
ret
=
-
ENOMEM
;
goto
err_mux_alloc
;
}
data
->
muxc
->
priv
=
data
;
/* register muxed i2c channels. One for each supported battery */
for
(
i
=
0
;
i
<
SBSM_MAX_BATS
;
++
i
)
{
if
(
data
->
supported_bats
&
BIT
(
i
))
{
ret
=
i2c_mux_add_adapter
(
data
->
muxc
,
0
,
i
+
1
,
0
);
if
(
ret
)
break
;
}
}
if
(
ret
)
{
dev_err
(
dev
,
"failed to register i2c mux channel %d
\n
"
,
i
+
1
);
goto
err_mux_register
;
}
psy_desc
=
devm_kmemdup
(
dev
,
&
sbsm_default_psy_desc
,
sizeof
(
struct
power_supply_desc
),
GFP_KERNEL
);
if
(
!
psy_desc
)
{
ret
=
-
ENOMEM
;
goto
err_psy
;
}
psy_desc
->
name
=
devm_kasprintf
(
dev
,
GFP_KERNEL
,
"sbsm-%s"
,
dev_name
(
&
client
->
dev
));
if
(
!
psy_desc
->
name
)
{
ret
=
-
ENOMEM
;
goto
err_psy
;
}
ret
=
sbsm_gpio_setup
(
data
);
if
(
ret
<
0
)
goto
err_psy
;
psy_cfg
.
drv_data
=
data
;
psy_cfg
.
of_node
=
dev
->
of_node
;
data
->
psy
=
devm_power_supply_register
(
dev
,
psy_desc
,
&
psy_cfg
);
if
(
IS_ERR
(
data
->
psy
))
{
ret
=
PTR_ERR
(
data
->
psy
);
dev_err
(
dev
,
"failed to register power supply %s
\n
"
,
psy_desc
->
name
);
goto
err_psy
;
}
return
0
;
err_psy:
err_mux_register:
i2c_mux_del_adapters
(
data
->
muxc
);
err_mux_alloc:
return
ret
;
}
static
int
sbsm_remove
(
struct
i2c_client
*
client
)
{
struct
sbsm_data
*
data
=
i2c_get_clientdata
(
client
);
i2c_mux_del_adapters
(
data
->
muxc
);
return
0
;
}
static
const
struct
i2c_device_id
sbsm_ids
[]
=
{
{
"sbs-manager"
,
0
},
{
"ltc1760"
,
0
},
{
}
};
MODULE_DEVICE_TABLE
(
i2c
,
sbsm_ids
);
#ifdef CONFIG_OF
static
const
struct
of_device_id
sbsm_dt_ids
[]
=
{
{
.
compatible
=
"sbs,sbs-manager"
},
{
.
compatible
=
"lltc,ltc1760"
},
{
}
};
MODULE_DEVICE_TABLE
(
of
,
sbsm_dt_ids
);
#endif
static
struct
i2c_driver
sbsm_driver
=
{
.
driver
=
{
.
name
=
"sbsm"
,
.
of_match_table
=
of_match_ptr
(
sbsm_dt_ids
),
},
.
probe
=
sbsm_probe
,
.
remove
=
sbsm_remove
,
.
alert
=
sbsm_alert
,
.
id_table
=
sbsm_ids
};
module_i2c_driver
(
sbsm_driver
);
MODULE_LICENSE
(
"GPL"
);
MODULE_AUTHOR
(
"Karl-Heinz Schneider <karl-heinz@schneider-inet.de>"
);
MODULE_DESCRIPTION
(
"SBSM Smart Battery System Manager"
);
include/linux/i2c-smbus.h
浏览文件 @
f6d29536
...
...
@@ -42,7 +42,6 @@
* properly set.
*/
struct
i2c_smbus_alert_setup
{
unsigned
int
alert_edge_triggered
:
1
;
int
irq
;
};
...
...
@@ -50,4 +49,13 @@ struct i2c_client *i2c_setup_smbus_alert(struct i2c_adapter *adapter,
struct
i2c_smbus_alert_setup
*
setup
);
int
i2c_handle_smbus_alert
(
struct
i2c_client
*
ara
);
#if IS_ENABLED(CONFIG_I2C_SMBUS) && IS_ENABLED(CONFIG_OF)
int
of_i2c_setup_smbus_alert
(
struct
i2c_adapter
*
adap
);
#else
static
inline
int
of_i2c_setup_smbus_alert
(
struct
i2c_adapter
*
adap
)
{
return
0
;
}
#endif
#endif
/* _LINUX_I2C_SMBUS_H */
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录