Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
OpenHarmony
kernel_linux
提交
350fe5ee
K
kernel_linux
项目概览
OpenHarmony
/
kernel_linux
上一次同步 4 年多
通知
15
Star
8
Fork
2
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
K
kernel_linux
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
350fe5ee
编写于
5月 23, 2017
作者:
L
Linus Walleij
浏览文件
操作
浏览文件
下载
差异文件
Merge branch 'mcp23s08' of /home/linus/linux-pinctrl into devel
上级
39d2675a
7f38c5b9
变更
10
显示空白变更内容
内联
并排
Showing
10 changed file
with
425 addition
and
308 deletion
+425
-308
arch/arm/configs/lpc32xx_defconfig
arch/arm/configs/lpc32xx_defconfig
+1
-1
arch/blackfin/configs/BF609-EZKIT_defconfig
arch/blackfin/configs/BF609-EZKIT_defconfig
+1
-1
arch/blackfin/mach-bf527/boards/tll6527m.c
arch/blackfin/mach-bf527/boards/tll6527m.c
+4
-4
arch/blackfin/mach-bf609/boards/ezkit.c
arch/blackfin/mach-bf609/boards/ezkit.c
+2
-2
drivers/gpio/Kconfig
drivers/gpio/Kconfig
+0
-17
drivers/gpio/Makefile
drivers/gpio/Makefile
+0
-1
drivers/pinctrl/Kconfig
drivers/pinctrl/Kconfig
+13
-0
drivers/pinctrl/Makefile
drivers/pinctrl/Makefile
+1
-0
drivers/pinctrl/pinctrl-mcp23s08.c
drivers/pinctrl/pinctrl-mcp23s08.c
+397
-250
include/linux/spi/mcp23s08.h
include/linux/spi/mcp23s08.h
+6
-32
未找到文件。
arch/arm/configs/lpc32xx_defconfig
浏览文件 @
350fe5ee
...
...
@@ -112,7 +112,7 @@ CONFIG_GPIO_SX150X=y
CONFIG_GPIO_74X164=y
CONFIG_GPIO_MAX7301=y
CONFIG_GPIO_MC33880=y
CONFIG_
GPIO
_MCP23S08=y
CONFIG_
PINCTRL
_MCP23S08=y
CONFIG_SENSORS_DS620=y
CONFIG_SENSORS_MAX6639=y
CONFIG_WATCHDOG=y
...
...
arch/blackfin/configs/BF609-EZKIT_defconfig
浏览文件 @
350fe5ee
...
...
@@ -105,7 +105,7 @@ CONFIG_SPI=y
CONFIG_SPI_ADI_V3=y
CONFIG_GPIOLIB=y
CONFIG_GPIO_SYSFS=y
CONFIG_
GPIO
_MCP23S08=y
CONFIG_
PINCTRL
_MCP23S08=y
# CONFIG_HWMON is not set
CONFIG_WATCHDOG=y
CONFIG_BFIN_WDT=y
...
...
arch/blackfin/mach-bf527/boards/tll6527m.c
浏览文件 @
350fe5ee
...
...
@@ -348,14 +348,14 @@ static struct platform_device bfin_i2s = {
};
#endif
#if IS_ENABLED(CONFIG_
GPIO
_MCP23S08)
#if IS_ENABLED(CONFIG_
PINCTRL
_MCP23S08)
#include <linux/spi/mcp23s08.h>
static
const
struct
mcp23s08_platform_data
bfin_mcp23s08_sys_gpio_info
=
{
.
chip
[
0
].
is_present
=
true
,
.
spi_present_mask
=
BIT
(
0
)
,
.
base
=
0x30
,
};
static
const
struct
mcp23s08_platform_data
bfin_mcp23s08_usr_gpio_info
=
{
.
chip
[
2
].
is_present
=
true
,
.
spi_present_mask
=
BIT
(
2
)
,
.
base
=
0x38
,
};
#endif
...
...
@@ -423,7 +423,7 @@ static struct spi_board_info bfin_spi_board_info[] __initdata = {
.
mode
=
SPI_CPHA
|
SPI_CPOL
,
},
#endif
#if IS_ENABLED(CONFIG_
GPIO
_MCP23S08)
#if IS_ENABLED(CONFIG_
PINCTRL
_MCP23S08)
{
.
modalias
=
"mcp23s08"
,
.
platform_data
=
&
bfin_mcp23s08_sys_gpio_info
,
...
...
arch/blackfin/mach-bf609/boards/ezkit.c
浏览文件 @
350fe5ee
...
...
@@ -1887,7 +1887,7 @@ static struct platform_device i2c_bfin_twi1_device = {
};
#endif
#if IS_ENABLED(CONFIG_
GPIO
_MCP23S08)
#if IS_ENABLED(CONFIG_
PINCTRL
_MCP23S08)
#include <linux/spi/mcp23s08.h>
static
const
struct
mcp23s08_platform_data
bfin_mcp23s08_soft_switch0
=
{
.
base
=
120
,
...
...
@@ -1929,7 +1929,7 @@ static struct i2c_board_info __initdata bfin_i2c_board_info0[] = {
I2C_BOARD_INFO
(
"ssm2602"
,
0x1b
),
},
#endif
#if IS_ENABLED(CONFIG_
GPIO
_MCP23S08)
#if IS_ENABLED(CONFIG_
PINCTRL
_MCP23S08)
{
I2C_BOARD_INFO
(
"mcp23017"
,
0x21
),
.
platform_data
=
(
void
*
)
&
bfin_mcp23s08_soft_switch0
...
...
drivers/gpio/Kconfig
浏览文件 @
350fe5ee
...
...
@@ -1232,23 +1232,6 @@ config GPIO_XRA1403
endmenu
menu "SPI or I2C GPIO expanders"
depends on (SPI_MASTER && !I2C) || I2C
config GPIO_MCP23S08
tristate "Microchip MCP23xxx I/O expander"
depends on OF_GPIO
select GPIOLIB_IRQCHIP
select REGMAP_I2C if I2C
select REGMAP if SPI_MASTER
help
SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017
I/O expanders.
This provides a GPIO interface supporting inputs and outputs.
The I2C versions of the chips can be used as interrupt-controller.
endmenu
menu "USB GPIO expanders"
depends on USB
...
...
drivers/gpio/Makefile
浏览文件 @
350fe5ee
...
...
@@ -77,7 +77,6 @@ obj-$(CONFIG_GPIO_MENZ127) += gpio-menz127.o
obj-$(CONFIG_GPIO_MERRIFIELD)
+=
gpio-merrifield.o
obj-$(CONFIG_GPIO_MC33880)
+=
gpio-mc33880.o
obj-$(CONFIG_GPIO_MC9S08DZ60)
+=
gpio-mc9s08dz60.o
obj-$(CONFIG_GPIO_MCP23S08)
+=
gpio-mcp23s08.o
obj-$(CONFIG_GPIO_ML_IOH)
+=
gpio-ml-ioh.o
obj-$(CONFIG_GPIO_MM_LANTIQ)
+=
gpio-mm-lantiq.o
obj-$(CONFIG_GPIO_MOCKUP)
+=
gpio-mockup.o
...
...
drivers/pinctrl/Kconfig
浏览文件 @
350fe5ee
...
...
@@ -146,6 +146,19 @@ config PINCTRL_FALCON
depends on SOC_FALCON
depends on PINCTRL_LANTIQ
config PINCTRL_MCP23S08
tristate "Microchip MCP23xxx I/O expander"
depends on SPI_MASTER || I2C
select GPIOLIB_IRQCHIP
select REGMAP_I2C if I2C
select REGMAP_SPI if SPI_MASTER
select GENERIC_PINCONF
help
SPI/I2C driver for Microchip MCP23S08/MCP23S17/MCP23008/MCP23017
I/O expanders.
This provides a GPIO interface supporting inputs and outputs.
The I2C versions of the chips can be used as interrupt-controller.
config PINCTRL_MESON
bool
depends on OF
...
...
drivers/pinctrl/Makefile
浏览文件 @
350fe5ee
...
...
@@ -19,6 +19,7 @@ obj-$(CONFIG_PINCTRL_DA850_PUPD) += pinctrl-da850-pupd.o
obj-$(CONFIG_PINCTRL_DIGICOLOR)
+=
pinctrl-digicolor.o
obj-$(CONFIG_PINCTRL_FALCON)
+=
pinctrl-falcon.o
obj-$(CONFIG_PINCTRL_MAX77620)
+=
pinctrl-max77620.o
obj-$(CONFIG_PINCTRL_MCP23S08)
+=
pinctrl-mcp23s08.o
obj-$(CONFIG_PINCTRL_MESON)
+=
meson/
obj-$(CONFIG_PINCTRL_OXNAS)
+=
pinctrl-oxnas.o
obj-$(CONFIG_PINCTRL_PALMAS)
+=
pinctrl-palmas.o
...
...
drivers/
gpio/gpio
-mcp23s08.c
→
drivers/
pinctrl/pinctrl
-mcp23s08.c
浏览文件 @
350fe5ee
/*
* MCP23S08 SPI/I2C GPIO gpio expander driver
*
* The inputs and outputs of the mcp23s08, mcp23s17, mcp23008 and mcp23017 are
* supported.
* For the I2C versions of the chips (mcp23008 and mcp23017) generation of
* interrupts is also supported.
* The hardware of the SPI versions of the chips (mcp23s08 and mcp23s17) is
* also capable of generating interrupts, but the linux driver does not
* support that yet.
*/
/* MCP23S08 SPI/I2C GPIO driver */
#include <linux/kernel.h>
#include <linux/device.h>
...
...
@@ -21,11 +11,13 @@
#include <linux/slab.h>
#include <asm/byteorder.h>
#include <linux/interrupt.h>
#include <linux/of_irq.h>
#include <linux/of_device.h>
#include <linux/regmap.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinconf-generic.h>
/*
*
/*
* MCP types supported by driver
*/
#define MCP_TYPE_S08 0
...
...
@@ -34,6 +26,8 @@
#define MCP_TYPE_017 3
#define MCP_TYPE_S18 4
#define MCP_MAX_DEV_PER_CS 8
/* Registers are all 8 bits wide.
*
* The mcp23s17 has twice as many bits, and can be configured to work
...
...
@@ -64,19 +58,52 @@ struct mcp23s08 {
bool
irq_active_high
;
bool
reg_shift
;
u16
cache
[
11
];
u16
irq_rise
;
u16
irq_fall
;
int
irq
;
bool
irq_controller
;
/* lock protects the cached values */
int
cached_gpio
;
/* lock protects regmap access with bypass/cache flags */
struct
mutex
lock
;
struct
mutex
irq_lock
;
struct
gpio_chip
chip
;
struct
regmap
*
regmap
;
struct
device
*
dev
;
struct
pinctrl_dev
*
pctldev
;
struct
pinctrl_desc
pinctrl_desc
;
};
static
const
struct
reg_default
mcp23x08_defaults
[]
=
{
{.
reg
=
MCP_IODIR
,
.
def
=
0xff
},
{.
reg
=
MCP_IPOL
,
.
def
=
0x00
},
{.
reg
=
MCP_GPINTEN
,
.
def
=
0x00
},
{.
reg
=
MCP_DEFVAL
,
.
def
=
0x00
},
{.
reg
=
MCP_INTCON
,
.
def
=
0x00
},
{.
reg
=
MCP_IOCON
,
.
def
=
0x00
},
{.
reg
=
MCP_GPPU
,
.
def
=
0x00
},
{.
reg
=
MCP_OLAT
,
.
def
=
0x00
},
};
static
const
struct
regmap_range
mcp23x08_volatile_range
=
{
.
range_min
=
MCP_INTF
,
.
range_max
=
MCP_GPIO
,
};
static
const
struct
regmap_access_table
mcp23x08_volatile_table
=
{
.
yes_ranges
=
&
mcp23x08_volatile_range
,
.
n_yes_ranges
=
1
,
};
static
const
struct
regmap_range
mcp23x08_precious_range
=
{
.
range_min
=
MCP_GPIO
,
.
range_max
=
MCP_GPIO
,
};
static
const
struct
regmap_access_table
mcp23x08_precious_table
=
{
.
yes_ranges
=
&
mcp23x08_precious_range
,
.
n_yes_ranges
=
1
,
};
static
const
struct
regmap_config
mcp23x08_regmap
=
{
...
...
@@ -84,18 +111,203 @@ static const struct regmap_config mcp23x08_regmap = {
.
val_bits
=
8
,
.
reg_stride
=
1
,
.
volatile_table
=
&
mcp23x08_volatile_table
,
.
precious_table
=
&
mcp23x08_precious_table
,
.
reg_defaults
=
mcp23x08_defaults
,
.
num_reg_defaults
=
ARRAY_SIZE
(
mcp23x08_defaults
),
.
cache_type
=
REGCACHE_FLAT
,
.
max_register
=
MCP_OLAT
,
};
static
const
struct
reg_default
mcp23x16_defaults
[]
=
{
{.
reg
=
MCP_IODIR
<<
1
,
.
def
=
0xffff
},
{.
reg
=
MCP_IPOL
<<
1
,
.
def
=
0x0000
},
{.
reg
=
MCP_GPINTEN
<<
1
,
.
def
=
0x0000
},
{.
reg
=
MCP_DEFVAL
<<
1
,
.
def
=
0x0000
},
{.
reg
=
MCP_INTCON
<<
1
,
.
def
=
0x0000
},
{.
reg
=
MCP_IOCON
<<
1
,
.
def
=
0x0000
},
{.
reg
=
MCP_GPPU
<<
1
,
.
def
=
0x0000
},
{.
reg
=
MCP_OLAT
<<
1
,
.
def
=
0x0000
},
};
static
const
struct
regmap_range
mcp23x16_volatile_range
=
{
.
range_min
=
MCP_INTF
<<
1
,
.
range_max
=
MCP_GPIO
<<
1
,
};
static
const
struct
regmap_access_table
mcp23x16_volatile_table
=
{
.
yes_ranges
=
&
mcp23x16_volatile_range
,
.
n_yes_ranges
=
1
,
};
static
const
struct
regmap_range
mcp23x16_precious_range
=
{
.
range_min
=
MCP_GPIO
<<
1
,
.
range_max
=
MCP_GPIO
<<
1
,
};
static
const
struct
regmap_access_table
mcp23x16_precious_table
=
{
.
yes_ranges
=
&
mcp23x16_precious_range
,
.
n_yes_ranges
=
1
,
};
static
const
struct
regmap_config
mcp23x17_regmap
=
{
.
reg_bits
=
8
,
.
val_bits
=
16
,
.
reg_stride
=
2
,
.
max_register
=
MCP_OLAT
<<
1
,
.
volatile_table
=
&
mcp23x16_volatile_table
,
.
precious_table
=
&
mcp23x16_precious_table
,
.
reg_defaults
=
mcp23x16_defaults
,
.
num_reg_defaults
=
ARRAY_SIZE
(
mcp23x16_defaults
),
.
cache_type
=
REGCACHE_FLAT
,
.
val_format_endian
=
REGMAP_ENDIAN_LITTLE
,
};
static
int
mcp_read
(
struct
mcp23s08
*
mcp
,
unsigned
int
reg
,
unsigned
int
*
val
)
{
return
regmap_read
(
mcp
->
regmap
,
reg
<<
mcp
->
reg_shift
,
val
);
}
static
int
mcp_write
(
struct
mcp23s08
*
mcp
,
unsigned
int
reg
,
unsigned
int
val
)
{
return
regmap_write
(
mcp
->
regmap
,
reg
<<
mcp
->
reg_shift
,
val
);
}
static
int
mcp_set_mask
(
struct
mcp23s08
*
mcp
,
unsigned
int
reg
,
unsigned
int
mask
,
bool
enabled
)
{
u16
val
=
enabled
?
0xffff
:
0x0000
;
return
regmap_update_bits
(
mcp
->
regmap
,
reg
<<
mcp
->
reg_shift
,
mask
,
val
);
}
static
int
mcp_set_bit
(
struct
mcp23s08
*
mcp
,
unsigned
int
reg
,
unsigned
int
pin
,
bool
enabled
)
{
u16
mask
=
BIT
(
pin
);
return
mcp_set_mask
(
mcp
,
reg
,
mask
,
enabled
);
}
static
const
struct
pinctrl_pin_desc
mcp23x08_pins
[]
=
{
PINCTRL_PIN
(
0
,
"gpio0"
),
PINCTRL_PIN
(
1
,
"gpio1"
),
PINCTRL_PIN
(
2
,
"gpio2"
),
PINCTRL_PIN
(
3
,
"gpio3"
),
PINCTRL_PIN
(
4
,
"gpio4"
),
PINCTRL_PIN
(
5
,
"gpio5"
),
PINCTRL_PIN
(
6
,
"gpio6"
),
PINCTRL_PIN
(
7
,
"gpio7"
),
};
static
const
struct
pinctrl_pin_desc
mcp23x17_pins
[]
=
{
PINCTRL_PIN
(
0
,
"gpio0"
),
PINCTRL_PIN
(
1
,
"gpio1"
),
PINCTRL_PIN
(
2
,
"gpio2"
),
PINCTRL_PIN
(
3
,
"gpio3"
),
PINCTRL_PIN
(
4
,
"gpio4"
),
PINCTRL_PIN
(
5
,
"gpio5"
),
PINCTRL_PIN
(
6
,
"gpio6"
),
PINCTRL_PIN
(
7
,
"gpio7"
),
PINCTRL_PIN
(
8
,
"gpio8"
),
PINCTRL_PIN
(
9
,
"gpio9"
),
PINCTRL_PIN
(
10
,
"gpio10"
),
PINCTRL_PIN
(
11
,
"gpio11"
),
PINCTRL_PIN
(
12
,
"gpio12"
),
PINCTRL_PIN
(
13
,
"gpio13"
),
PINCTRL_PIN
(
14
,
"gpio14"
),
PINCTRL_PIN
(
15
,
"gpio15"
),
};
static
int
mcp_pinctrl_get_groups_count
(
struct
pinctrl_dev
*
pctldev
)
{
return
0
;
}
static
const
char
*
mcp_pinctrl_get_group_name
(
struct
pinctrl_dev
*
pctldev
,
unsigned
int
group
)
{
return
NULL
;
}
static
int
mcp_pinctrl_get_group_pins
(
struct
pinctrl_dev
*
pctldev
,
unsigned
int
group
,
const
unsigned
int
**
pins
,
unsigned
int
*
num_pins
)
{
return
-
ENOTSUPP
;
}
static
const
struct
pinctrl_ops
mcp_pinctrl_ops
=
{
.
get_groups_count
=
mcp_pinctrl_get_groups_count
,
.
get_group_name
=
mcp_pinctrl_get_group_name
,
.
get_group_pins
=
mcp_pinctrl_get_group_pins
,
#ifdef CONFIG_OF
.
dt_node_to_map
=
pinconf_generic_dt_node_to_map_pin
,
.
dt_free_map
=
pinconf_generic_dt_free_map
,
#endif
};
static
int
mcp_pinconf_get
(
struct
pinctrl_dev
*
pctldev
,
unsigned
int
pin
,
unsigned
long
*
config
)
{
struct
mcp23s08
*
mcp
=
pinctrl_dev_get_drvdata
(
pctldev
);
enum
pin_config_param
param
=
pinconf_to_config_param
(
*
config
);
unsigned
int
data
,
status
;
int
ret
;
switch
(
param
)
{
case
PIN_CONFIG_BIAS_PULL_UP
:
ret
=
mcp_read
(
mcp
,
MCP_GPPU
,
&
data
);
if
(
ret
<
0
)
return
ret
;
status
=
(
data
&
BIT
(
pin
))
?
1
:
0
;
break
;
default:
dev_err
(
mcp
->
dev
,
"Invalid config param %04x
\n
"
,
param
);
return
-
ENOTSUPP
;
}
*
config
=
0
;
return
status
?
0
:
-
EINVAL
;
}
static
int
mcp_pinconf_set
(
struct
pinctrl_dev
*
pctldev
,
unsigned
int
pin
,
unsigned
long
*
configs
,
unsigned
int
num_configs
)
{
struct
mcp23s08
*
mcp
=
pinctrl_dev_get_drvdata
(
pctldev
);
enum
pin_config_param
param
;
u32
arg
,
mask
;
u16
val
;
int
ret
=
0
;
int
i
;
for
(
i
=
0
;
i
<
num_configs
;
i
++
)
{
param
=
pinconf_to_config_param
(
configs
[
i
]);
arg
=
pinconf_to_config_argument
(
configs
[
i
]);
switch
(
param
)
{
case
PIN_CONFIG_BIAS_PULL_UP
:
val
=
arg
?
0xFFFF
:
0x0000
;
mask
=
BIT
(
pin
);
ret
=
mcp_set_bit
(
mcp
,
MCP_GPPU
,
pin
,
arg
);
break
;
default:
dev_err
(
mcp
->
dev
,
"Invalid config param %04x
\n
"
,
param
);
return
-
ENOTSUPP
;
}
}
return
ret
;
}
static
const
struct
pinconf_ops
mcp_pinconf_ops
=
{
.
pin_config_get
=
mcp_pinconf_get
,
.
pin_config_set
=
mcp_pinconf_set
,
.
is_generic
=
true
,
};
/*----------------------------------------------------------------------*/
#ifdef CONFIG_SPI_MASTER
...
...
@@ -158,30 +370,6 @@ static const struct regmap_bus mcp23sxx_spi_regmap = {
#endif
/* CONFIG_SPI_MASTER */
static
int
mcp_read
(
struct
mcp23s08
*
mcp
,
unsigned
int
reg
,
unsigned
int
*
val
)
{
return
regmap_read
(
mcp
->
regmap
,
reg
<<
mcp
->
reg_shift
,
val
);
}
static
int
mcp_write
(
struct
mcp23s08
*
mcp
,
unsigned
int
reg
,
unsigned
int
val
)
{
return
regmap_write
(
mcp
->
regmap
,
reg
<<
mcp
->
reg_shift
,
val
);
}
static
int
mcp_update_cache
(
struct
mcp23s08
*
mcp
)
{
int
ret
,
reg
,
i
;
for
(
i
=
0
;
i
<
ARRAY_SIZE
(
mcp
->
cache
);
i
++
)
{
ret
=
mcp_read
(
mcp
,
i
,
&
reg
);
if
(
ret
<
0
)
return
ret
;
mcp
->
cache
[
i
]
=
reg
;
}
return
0
;
}
/*----------------------------------------------------------------------*/
/* A given spi_device can represent up to eight mcp23sxx chips
...
...
@@ -202,9 +390,9 @@ static int mcp23s08_direction_input(struct gpio_chip *chip, unsigned offset)
int
status
;
mutex_lock
(
&
mcp
->
lock
);
mcp
->
cache
[
MCP_IODIR
]
|=
(
1
<<
offset
);
status
=
mcp_write
(
mcp
,
MCP_IODIR
,
mcp
->
cache
[
MCP_IODIR
]);
status
=
mcp_set_bit
(
mcp
,
MCP_IODIR
,
offset
,
true
);
mutex_unlock
(
&
mcp
->
lock
);
return
status
;
}
...
...
@@ -219,33 +407,27 @@ static int mcp23s08_get(struct gpio_chip *chip, unsigned offset)
ret
=
mcp_read
(
mcp
,
MCP_GPIO
,
&
status
);
if
(
ret
<
0
)
status
=
0
;
else
{
mcp
->
cache
[
MCP_GPIO
]
=
status
;
else
status
=
!!
(
status
&
(
1
<<
offset
));
}
mcp
->
cached_gpio
=
status
;
mutex_unlock
(
&
mcp
->
lock
);
return
status
;
}
static
int
__mcp23s08_set
(
struct
mcp23s08
*
mcp
,
unsigned
mask
,
int
value
)
static
int
__mcp23s08_set
(
struct
mcp23s08
*
mcp
,
unsigned
mask
,
bool
value
)
{
unsigned
olat
=
mcp
->
cache
[
MCP_OLAT
];
if
(
value
)
olat
|=
mask
;
else
olat
&=
~
mask
;
mcp
->
cache
[
MCP_OLAT
]
=
olat
;
return
mcp_write
(
mcp
,
MCP_OLAT
,
olat
);
return
mcp_set_mask
(
mcp
,
MCP_OLAT
,
mask
,
value
);
}
static
void
mcp23s08_set
(
struct
gpio_chip
*
chip
,
unsigned
offset
,
int
value
)
{
struct
mcp23s08
*
mcp
=
gpiochip_get_data
(
chip
);
unsigned
mask
=
1
<<
offset
;
unsigned
mask
=
BIT
(
offset
)
;
mutex_lock
(
&
mcp
->
lock
);
__mcp23s08_set
(
mcp
,
mask
,
value
);
__mcp23s08_set
(
mcp
,
mask
,
!!
value
);
mutex_unlock
(
&
mcp
->
lock
);
}
...
...
@@ -253,14 +435,13 @@ static int
mcp23s08_direction_output
(
struct
gpio_chip
*
chip
,
unsigned
offset
,
int
value
)
{
struct
mcp23s08
*
mcp
=
gpiochip_get_data
(
chip
);
unsigned
mask
=
1
<<
offset
;
unsigned
mask
=
BIT
(
offset
)
;
int
status
;
mutex_lock
(
&
mcp
->
lock
);
status
=
__mcp23s08_set
(
mcp
,
mask
,
value
);
if
(
status
==
0
)
{
mcp
->
cache
[
MCP_IODIR
]
&=
~
mask
;
status
=
mcp_write
(
mcp
,
MCP_IODIR
,
mcp
->
cache
[
MCP_IODIR
]);
status
=
mcp_set_mask
(
mcp
,
MCP_IODIR
,
mask
,
false
);
}
mutex_unlock
(
&
mcp
->
lock
);
return
status
;
...
...
@@ -270,7 +451,7 @@ mcp23s08_direction_output(struct gpio_chip *chip, unsigned offset, int value)
static
irqreturn_t
mcp23s08_irq
(
int
irq
,
void
*
data
)
{
struct
mcp23s08
*
mcp
=
data
;
int
intcap
,
int
f
,
i
,
gpio
,
gpio_orig
,
intcap_mask
;
int
intcap
,
int
con
,
intf
,
i
,
gpio
,
gpio_orig
,
intcap_mask
,
defval
;
unsigned
int
child_irq
;
bool
intf_set
,
intcap_changed
,
gpio_bit_changed
,
defval_changed
,
gpio_set
;
...
...
@@ -281,25 +462,31 @@ static irqreturn_t mcp23s08_irq(int irq, void *data)
return
IRQ_HANDLED
;
}
mcp
->
cache
[
MCP_INTF
]
=
intf
;
if
(
mcp_read
(
mcp
,
MCP_INTCAP
,
&
intcap
)
<
0
)
{
mutex_unlock
(
&
mcp
->
lock
);
return
IRQ_HANDLED
;
}
mcp
->
cache
[
MCP_INTCAP
]
=
intcap
;
if
(
mcp_read
(
mcp
,
MCP_INTCON
,
&
intcon
)
<
0
)
{
mutex_unlock
(
&
mcp
->
lock
);
return
IRQ_HANDLED
;
}
if
(
mcp_read
(
mcp
,
MCP_DEFVAL
,
&
defval
)
<
0
)
{
mutex_unlock
(
&
mcp
->
lock
);
return
IRQ_HANDLED
;
}
/* This clears the interrupt(configurable on S18) */
if
(
mcp_read
(
mcp
,
MCP_GPIO
,
&
gpio
)
<
0
)
{
mutex_unlock
(
&
mcp
->
lock
);
return
IRQ_HANDLED
;
}
gpio_orig
=
mcp
->
cache
[
MCP_GPIO
]
;
mcp
->
cache
[
MCP_GPIO
]
=
gpio
;
gpio_orig
=
mcp
->
cache
d_gpio
;
mcp
->
cache
d_gpio
=
gpio
;
mutex_unlock
(
&
mcp
->
lock
);
if
(
mcp
->
cache
[
MCP_INTF
]
==
0
)
{
if
(
intf
==
0
)
{
/* There is no interrupt pending */
return
IRQ_HANDLED
;
}
...
...
@@ -327,7 +514,7 @@ static irqreturn_t mcp23s08_irq(int irq, void *data)
* to see if the input has changed.
*/
intf_set
=
BIT
(
i
)
&
mcp
->
cache
[
MCP_INTF
]
;
intf_set
=
intf
&
BIT
(
i
)
;
if
(
i
<
8
&&
intf_set
)
intcap_mask
=
0x00FF
;
else
if
(
i
>=
8
&&
intf_set
)
...
...
@@ -336,14 +523,14 @@ static irqreturn_t mcp23s08_irq(int irq, void *data)
intcap_mask
=
0x00
;
intcap_changed
=
(
intcap_mask
&
(
BIT
(
i
)
&
mcp
->
cache
[
MCP_INTCAP
]
))
!=
(
intcap
&
BIT
(
i
)
))
!=
(
intcap_mask
&
(
BIT
(
i
)
&
gpio_orig
));
gpio_set
=
BIT
(
i
)
&
mcp
->
cache
[
MCP_GPIO
]
;
gpio_set
=
BIT
(
i
)
&
gpio
;
gpio_bit_changed
=
(
BIT
(
i
)
&
gpio_orig
)
!=
(
BIT
(
i
)
&
mcp
->
cache
[
MCP_GPIO
]
);
defval_changed
=
(
BIT
(
i
)
&
mcp
->
cache
[
MCP_INTCON
]
)
&&
((
BIT
(
i
)
&
mcp
->
cache
[
MCP_GPIO
]
)
!=
(
BIT
(
i
)
&
mcp
->
cache
[
MCP_DEFVAL
]
));
(
BIT
(
i
)
&
gpio
);
defval_changed
=
(
BIT
(
i
)
&
intcon
)
&&
((
BIT
(
i
)
&
gpio
)
!=
(
BIT
(
i
)
&
defval
));
if
(((
gpio_bit_changed
||
intcap_changed
)
&&
(
BIT
(
i
)
&
mcp
->
irq_rise
)
&&
gpio_set
)
||
...
...
@@ -364,7 +551,7 @@ static void mcp23s08_irq_mask(struct irq_data *data)
struct
mcp23s08
*
mcp
=
gpiochip_get_data
(
gc
);
unsigned
int
pos
=
data
->
hwirq
;
mcp
->
cache
[
MCP_GPINTEN
]
&=
~
BIT
(
pos
);
mcp
_set_bit
(
mcp
,
MCP_GPINTEN
,
pos
,
false
);
}
static
void
mcp23s08_irq_unmask
(
struct
irq_data
*
data
)
...
...
@@ -373,7 +560,7 @@ static void mcp23s08_irq_unmask(struct irq_data *data)
struct
mcp23s08
*
mcp
=
gpiochip_get_data
(
gc
);
unsigned
int
pos
=
data
->
hwirq
;
mcp
->
cache
[
MCP_GPINTEN
]
|=
BIT
(
pos
);
mcp
_set_bit
(
mcp
,
MCP_GPINTEN
,
pos
,
true
);
}
static
int
mcp23s08_irq_set_type
(
struct
irq_data
*
data
,
unsigned
int
type
)
...
...
@@ -384,23 +571,23 @@ static int mcp23s08_irq_set_type(struct irq_data *data, unsigned int type)
int
status
=
0
;
if
((
type
&
IRQ_TYPE_EDGE_BOTH
)
==
IRQ_TYPE_EDGE_BOTH
)
{
mcp
->
cache
[
MCP_INTCON
]
&=
~
BIT
(
pos
);
mcp
_set_bit
(
mcp
,
MCP_INTCON
,
pos
,
false
);
mcp
->
irq_rise
|=
BIT
(
pos
);
mcp
->
irq_fall
|=
BIT
(
pos
);
}
else
if
(
type
&
IRQ_TYPE_EDGE_RISING
)
{
mcp
->
cache
[
MCP_INTCON
]
&=
~
BIT
(
pos
);
mcp
_set_bit
(
mcp
,
MCP_INTCON
,
pos
,
false
);
mcp
->
irq_rise
|=
BIT
(
pos
);
mcp
->
irq_fall
&=
~
BIT
(
pos
);
}
else
if
(
type
&
IRQ_TYPE_EDGE_FALLING
)
{
mcp
->
cache
[
MCP_INTCON
]
&=
~
BIT
(
pos
);
mcp
_set_bit
(
mcp
,
MCP_INTCON
,
pos
,
false
);
mcp
->
irq_rise
&=
~
BIT
(
pos
);
mcp
->
irq_fall
|=
BIT
(
pos
);
}
else
if
(
type
&
IRQ_TYPE_LEVEL_HIGH
)
{
mcp
->
cache
[
MCP_INTCON
]
|=
BIT
(
pos
);
mcp
->
cache
[
MCP_DEFVAL
]
&=
~
BIT
(
pos
);
mcp
_set_bit
(
mcp
,
MCP_INTCON
,
pos
,
true
);
mcp
_set_bit
(
mcp
,
MCP_DEFVAL
,
pos
,
false
);
}
else
if
(
type
&
IRQ_TYPE_LEVEL_LOW
)
{
mcp
->
cache
[
MCP_INTCON
]
|=
BIT
(
pos
);
mcp
->
cache
[
MCP_DEFVAL
]
|=
BIT
(
pos
);
mcp
_set_bit
(
mcp
,
MCP_INTCON
,
pos
,
true
);
mcp
_set_bit
(
mcp
,
MCP_DEFVAL
,
pos
,
true
);
}
else
return
-
EINVAL
;
...
...
@@ -412,7 +599,8 @@ static void mcp23s08_irq_bus_lock(struct irq_data *data)
struct
gpio_chip
*
gc
=
irq_data_get_irq_chip_data
(
data
);
struct
mcp23s08
*
mcp
=
gpiochip_get_data
(
gc
);
mutex_lock
(
&
mcp
->
irq_lock
);
mutex_lock
(
&
mcp
->
lock
);
regcache_cache_only
(
mcp
->
regmap
,
true
);
}
static
void
mcp23s08_irq_bus_unlock
(
struct
irq_data
*
data
)
...
...
@@ -420,12 +608,10 @@ static void mcp23s08_irq_bus_unlock(struct irq_data *data)
struct
gpio_chip
*
gc
=
irq_data_get_irq_chip_data
(
data
);
struct
mcp23s08
*
mcp
=
gpiochip_get_data
(
gc
);
mutex_lock
(
&
mcp
->
lock
);
mcp_write
(
mcp
,
MCP_GPINTEN
,
mcp
->
cache
[
MCP_GPINTEN
]);
mcp_write
(
mcp
,
MCP_DEFVAL
,
mcp
->
cache
[
MCP_DEFVAL
]);
mcp_write
(
mcp
,
MCP_INTCON
,
mcp
->
cache
[
MCP_INTCON
]);
regcache_cache_only
(
mcp
->
regmap
,
false
);
regcache_sync
(
mcp
->
regmap
);
mutex_unlock
(
&
mcp
->
lock
);
mutex_unlock
(
&
mcp
->
irq_lock
);
}
static
struct
irq_chip
mcp23s08_irq_chip
=
{
...
...
@@ -443,8 +629,6 @@ static int mcp23s08_irq_setup(struct mcp23s08 *mcp)
int
err
;
unsigned
long
irqflags
=
IRQF_ONESHOT
|
IRQF_SHARED
;
mutex_init
(
&
mcp
->
irq_lock
);
if
(
mcp
->
irq_active_high
)
irqflags
|=
IRQF_TRIGGER_HIGH
;
else
...
...
@@ -483,6 +667,47 @@ static int mcp23s08_irq_setup(struct mcp23s08 *mcp)
#include <linux/seq_file.h>
/*
* This compares the chip's registers with the register
* cache and corrects any incorrectly set register. This
* can be used to fix state for MCP23xxx, that temporary
* lost its power supply.
*/
#define MCP23S08_CONFIG_REGS 8
static
int
__check_mcp23s08_reg_cache
(
struct
mcp23s08
*
mcp
)
{
int
cached
[
MCP23S08_CONFIG_REGS
];
int
err
=
0
,
i
;
/* read cached config registers */
for
(
i
=
0
;
i
<
MCP23S08_CONFIG_REGS
;
i
++
)
{
err
=
mcp_read
(
mcp
,
i
,
&
cached
[
i
]);
if
(
err
)
goto
out
;
}
regcache_cache_bypass
(
mcp
->
regmap
,
true
);
for
(
i
=
0
;
i
<
MCP23S08_CONFIG_REGS
;
i
++
)
{
int
uncached
;
err
=
mcp_read
(
mcp
,
i
,
&
uncached
);
if
(
err
)
goto
out
;
if
(
uncached
!=
cached
[
i
])
{
dev_err
(
mcp
->
dev
,
"restoring reg 0x%02x from 0x%04x to 0x%04x (power-loss?)
\n
"
,
i
,
uncached
,
cached
[
i
]);
mcp_write
(
mcp
,
i
,
cached
[
i
]);
}
}
out:
if
(
err
)
dev_err
(
mcp
->
dev
,
"read error: reg=%02x, err=%d"
,
i
,
err
);
regcache_cache_bypass
(
mcp
->
regmap
,
false
);
return
err
;
}
/*
* This shows more info than the generic gpio dump code:
* pullups, deglitching, open drain drive.
...
...
@@ -493,6 +718,7 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip)
char
bank
;
int
t
;
unsigned
mask
;
int
iodir
,
gpio
,
gppu
;
mcp
=
gpiochip_get_data
(
chip
);
...
...
@@ -500,13 +726,29 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip)
bank
=
'0'
+
((
mcp
->
addr
>>
1
)
&
0x7
);
mutex_lock
(
&
mcp
->
lock
);
t
=
mcp_update_cache
(
mcp
);
if
(
t
<
0
)
{
seq_printf
(
s
,
" I/O ERROR %d
\n
"
,
t
);
t
=
__check_mcp23s08_reg_cache
(
mcp
);
if
(
t
)
{
seq_printf
(
s
,
" I/O Error
\n
"
);
goto
done
;
}
t
=
mcp_read
(
mcp
,
MCP_IODIR
,
&
iodir
);
if
(
t
)
{
seq_printf
(
s
,
" I/O Error
\n
"
);
goto
done
;
}
t
=
mcp_read
(
mcp
,
MCP_GPIO
,
&
gpio
);
if
(
t
)
{
seq_printf
(
s
,
" I/O Error
\n
"
);
goto
done
;
}
t
=
mcp_read
(
mcp
,
MCP_GPPU
,
&
gppu
);
if
(
t
)
{
seq_printf
(
s
,
" I/O Error
\n
"
);
goto
done
;
}
for
(
t
=
0
,
mask
=
1
;
t
<
chip
->
ngpio
;
t
++
,
mask
<<=
1
)
{
for
(
t
=
0
,
mask
=
BIT
(
0
)
;
t
<
chip
->
ngpio
;
t
++
,
mask
<<=
1
)
{
const
char
*
label
;
label
=
gpiochip_is_requested
(
chip
,
t
);
...
...
@@ -515,9 +757,9 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip)
seq_printf
(
s
,
" gpio-%-3d P%c.%d (%-12s) %s %s %s"
,
chip
->
base
+
t
,
bank
,
t
,
label
,
(
mcp
->
cache
[
MCP_IODIR
]
&
mask
)
?
"in "
:
"out"
,
(
mcp
->
cache
[
MCP_GPIO
]
&
mask
)
?
"hi"
:
"lo"
,
(
mcp
->
cache
[
MCP_GPPU
]
&
mask
)
?
"up"
:
" "
);
(
iodir
&
mask
)
?
"in "
:
"out"
,
(
gpio
&
mask
)
?
"hi"
:
"lo"
,
(
gppu
&
mask
)
?
"up"
:
" "
);
/* NOTE: ignoring the irq-related registers */
seq_puts
(
s
,
"
\n
"
);
}
...
...
@@ -533,7 +775,7 @@ static void mcp23s08_dbg_show(struct seq_file *s, struct gpio_chip *chip)
static
int
mcp23s08_probe_one
(
struct
mcp23s08
*
mcp
,
struct
device
*
dev
,
void
*
data
,
unsigned
addr
,
unsigned
type
,
struct
mcp23s08_platform_data
*
pdata
,
int
cs
)
unsigned
int
base
,
int
cs
)
{
int
status
,
ret
;
bool
mirror
=
false
;
...
...
@@ -605,7 +847,7 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
if
(
IS_ERR
(
mcp
->
regmap
))
return
PTR_ERR
(
mcp
->
regmap
);
mcp
->
chip
.
base
=
pdata
->
base
;
mcp
->
chip
.
base
=
base
;
mcp
->
chip
.
can_sleep
=
true
;
mcp
->
chip
.
parent
=
dev
;
mcp
->
chip
.
owner
=
THIS_MODULE
;
...
...
@@ -618,13 +860,14 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
if
(
ret
<
0
)
goto
fail
;
mcp
->
irq_controller
=
pdata
->
irq_controller
;
mcp
->
irq_controller
=
device_property_read_bool
(
dev
,
"interrupt-controller"
);
if
(
mcp
->
irq
&&
mcp
->
irq_controller
)
{
mcp
->
irq_active_high
=
of_property_read_bool
(
mcp
->
chip
.
parent
->
of_node
,
device_property_read_bool
(
dev
,
"microchip,irq-active-high"
);
mirror
=
pdata
->
mirror
;
mirror
=
device_property_read_bool
(
dev
,
"microchip,irq-mirror"
)
;
}
if
((
status
&
IOCON_SEQOP
)
||
!
(
status
&
IOCON_HAEN
)
||
mirror
||
...
...
@@ -648,40 +891,32 @@ static int mcp23s08_probe_one(struct mcp23s08 *mcp, struct device *dev,
goto
fail
;
}
/* configure ~100K pullups */
ret
=
mcp_write
(
mcp
,
MCP_GPPU
,
pdata
->
chip
[
cs
].
pullups
);
ret
=
devm_gpiochip_add_data
(
dev
,
&
mcp
->
chip
,
mcp
);
if
(
ret
<
0
)
goto
fail
;
ret
=
mcp_update_cache
(
mcp
);
if
(
ret
<
0
)
goto
fail
;
/* disable inverter on input */
if
(
mcp
->
cache
[
MCP_IPOL
]
!=
0
)
{
mcp
->
cache
[
MCP_IPOL
]
=
0
;
ret
=
mcp_write
(
mcp
,
MCP_IPOL
,
0
);
if
(
ret
<
0
)
if
(
mcp
->
irq
&&
mcp
->
irq_controller
)
{
ret
=
mcp23s08_irq_setup
(
mcp
);
if
(
ret
)
goto
fail
;
}
/* disable irqs */
if
(
mcp
->
cache
[
MCP_GPINTEN
]
!=
0
)
{
mcp
->
cache
[
MCP_GPINTEN
]
=
0
;
ret
=
mcp_write
(
mcp
,
MCP_GPINTEN
,
0
);
if
(
ret
<
0
)
mcp
->
pinctrl_desc
.
name
=
"mcp23xxx-pinctrl"
;
mcp
->
pinctrl_desc
.
pctlops
=
&
mcp_pinctrl_ops
;
mcp
->
pinctrl_desc
.
confops
=
&
mcp_pinconf_ops
;
mcp
->
pinctrl_desc
.
npins
=
mcp
->
chip
.
ngpio
;
if
(
mcp
->
pinctrl_desc
.
npins
==
8
)
mcp
->
pinctrl_desc
.
pins
=
mcp23x08_pins
;
else
if
(
mcp
->
pinctrl_desc
.
npins
==
16
)
mcp
->
pinctrl_desc
.
pins
=
mcp23x17_pins
;
mcp
->
pinctrl_desc
.
owner
=
THIS_MODULE
;
mcp
->
pctldev
=
devm_pinctrl_register
(
dev
,
&
mcp
->
pinctrl_desc
,
mcp
);
if
(
IS_ERR
(
mcp
->
pctldev
))
{
ret
=
PTR_ERR
(
mcp
->
pctldev
);
goto
fail
;
}
ret
=
gpiochip_add_data
(
&
mcp
->
chip
,
mcp
);
if
(
ret
<
0
)
goto
fail
;
if
(
mcp
->
irq
&&
mcp
->
irq_controller
)
{
ret
=
mcp23s08_irq_setup
(
mcp
);
if
(
ret
)
goto
fail
;
}
fail:
if
(
ret
<
0
)
dev_dbg
(
dev
,
"can't setup chip %d, --> %d
\n
"
,
addr
,
ret
);
...
...
@@ -753,58 +988,24 @@ static int mcp230xx_probe(struct i2c_client *client,
struct
mcp23s08_platform_data
*
pdata
,
local_pdata
;
struct
mcp23s08
*
mcp
;
int
status
;
const
struct
of_device_id
*
match
;
match
=
of_match_device
(
of_match_ptr
(
mcp23s08_i2c_of_match
),
&
client
->
dev
);
if
(
match
)
{
pdata
=
&
local_pdata
;
pdata
->
base
=
-
1
;
pdata
->
chip
[
0
].
pullups
=
0
;
pdata
->
irq_controller
=
of_property_read_bool
(
client
->
dev
.
of_node
,
"interrupt-controller"
);
pdata
->
mirror
=
of_property_read_bool
(
client
->
dev
.
of_node
,
"microchip,irq-mirror"
);
client
->
irq
=
irq_of_parse_and_map
(
client
->
dev
.
of_node
,
0
);
}
else
{
pdata
=
dev_get_platdata
(
&
client
->
dev
);
if
(
!
pdata
)
{
pdata
=
devm_kzalloc
(
&
client
->
dev
,
sizeof
(
struct
mcp23s08_platform_data
),
GFP_KERNEL
);
if
(
!
pdata
)
return
-
ENOMEM
;
pdata
=
&
local_pdata
;
pdata
->
base
=
-
1
;
}
}
mcp
=
kzalloc
(
sizeof
(
*
mcp
),
GFP_KERNEL
);
mcp
=
devm_kzalloc
(
&
client
->
dev
,
sizeof
(
*
mcp
),
GFP_KERNEL
);
if
(
!
mcp
)
return
-
ENOMEM
;
mcp
->
irq
=
client
->
irq
;
status
=
mcp23s08_probe_one
(
mcp
,
&
client
->
dev
,
client
,
client
->
addr
,
id
->
driver_data
,
pdata
,
0
);
id
->
driver_data
,
pdata
->
base
,
0
);
if
(
status
)
goto
fail
;
i2c_set_clientdata
(
client
,
mcp
);
return
0
;
fail:
kfree
(
mcp
);
return
status
;
}
static
int
mcp230xx_remove
(
struct
i2c_client
*
client
)
{
struct
mcp23s08
*
mcp
=
i2c_get_clientdata
(
client
);
gpiochip_remove
(
&
mcp
->
chip
);
kfree
(
mcp
);
i2c_set_clientdata
(
client
,
mcp
);
return
0
;
}
...
...
@@ -822,7 +1023,6 @@ static struct i2c_driver mcp230xx_driver = {
.
of_match_table
=
of_match_ptr
(
mcp23s08_i2c_of_match
),
},
.
probe
=
mcp230xx_probe
,
.
remove
=
mcp230xx_remove
,
.
id_table
=
mcp230xx_id
,
};
...
...
@@ -856,60 +1056,40 @@ static int mcp23s08_probe(struct spi_device *spi)
int
status
,
type
;
unsigned
ngpio
=
0
;
const
struct
of_device_id
*
match
;
u32
spi_present_mask
=
0
;
match
=
of_match_device
(
of_match_ptr
(
mcp23s08_spi_of_match
),
&
spi
->
dev
);
if
(
match
)
{
if
(
match
)
type
=
(
int
)(
uintptr_t
)
match
->
data
;
status
=
of_property_read_u32
(
spi
->
dev
.
of_node
,
"microchip,spi-present-mask"
,
&
spi_present_mask
);
else
type
=
spi_get_device_id
(
spi
)
->
driver_data
;
pdata
=
dev_get_platdata
(
&
spi
->
dev
);
if
(
!
pdata
)
{
pdata
=
&
local_pdata
;
pdata
->
base
=
-
1
;
status
=
device_property_read_u32
(
&
spi
->
dev
,
"microchip,spi-present-mask"
,
&
pdata
->
spi_present_mask
);
if
(
status
)
{
status
=
of_property_read_u32
(
spi
->
dev
.
of_node
,
"mcp,spi-present-mask"
,
&
spi_present_mask
);
status
=
device_property_read_u32
(
&
spi
->
dev
,
"mcp,spi-present-mask"
,
&
pdata
->
spi_present_mask
);
if
(
status
)
{
dev_err
(
&
spi
->
dev
,
"DT has no spi-present-mask
\n
"
);
dev_err
(
&
spi
->
dev
,
"missing spi-present-mask"
);
return
-
ENODEV
;
}
}
if
((
spi_present_mask
<=
0
)
||
(
spi_present_mask
>=
256
))
{
dev_err
(
&
spi
->
dev
,
"invalid spi-present-mask
\n
"
);
return
-
ENODEV
;
}
pdata
=
&
local_pdata
;
pdata
->
base
=
-
1
;
for
(
addr
=
0
;
addr
<
ARRAY_SIZE
(
pdata
->
chip
);
addr
++
)
{
pdata
->
chip
[
addr
].
pullups
=
0
;
if
(
spi_present_mask
&
(
1
<<
addr
))
chips
++
;
}
pdata
->
irq_controller
=
of_property_read_bool
(
spi
->
dev
.
of_node
,
"interrupt-controller"
);
pdata
->
mirror
=
of_property_read_bool
(
spi
->
dev
.
of_node
,
"microchip,irq-mirror"
);
}
else
{
type
=
spi_get_device_id
(
spi
)
->
driver_data
;
pdata
=
dev_get_platdata
(
&
spi
->
dev
);
if
(
!
pdata
)
{
pdata
=
devm_kzalloc
(
&
spi
->
dev
,
sizeof
(
struct
mcp23s08_platform_data
),
GFP_KERNEL
);
pdata
->
base
=
-
1
;
if
(
!
pdata
->
spi_present_mask
||
pdata
->
spi_present_mask
>
0xff
)
{
dev_err
(
&
spi
->
dev
,
"invalid spi-present-mask"
);
return
-
ENODEV
;
}
for
(
addr
=
0
;
addr
<
ARRAY_SIZE
(
pdata
->
chip
);
addr
++
)
{
if
(
!
pdata
->
chip
[
addr
].
is_present
)
continue
;
for
(
addr
=
0
;
addr
<
MCP_MAX_DEV_PER_CS
;
addr
++
)
{
if
(
pdata
->
spi_present_mask
&
BIT
(
addr
))
chips
++
;
if
((
type
==
MCP_TYPE_S08
)
&&
(
addr
>
3
))
{
dev_err
(
&
spi
->
dev
,
"mcp23s08 only supports address 0..3
\n
"
);
return
-
EINVAL
;
}
spi_present_mask
|=
1
<<
addr
;
}
}
if
(
!
chips
)
...
...
@@ -923,19 +1103,17 @@ static int mcp23s08_probe(struct spi_device *spi)
spi_set_drvdata
(
spi
,
data
);
spi
->
irq
=
irq_of_parse_and_map
(
spi
->
dev
.
of_node
,
0
);
for
(
addr
=
0
;
addr
<
ARRAY_SIZE
(
pdata
->
chip
);
addr
++
)
{
if
(
!
(
spi_present_mask
&
(
1
<<
addr
)))
for
(
addr
=
0
;
addr
<
MCP_MAX_DEV_PER_CS
;
addr
++
)
{
if
(
!
(
pdata
->
spi_present_mask
&
BIT
(
addr
)))
continue
;
chips
--
;
data
->
mcp
[
addr
]
=
&
data
->
chip
[
chips
];
data
->
mcp
[
addr
]
->
irq
=
spi
->
irq
;
status
=
mcp23s08_probe_one
(
data
->
mcp
[
addr
],
&
spi
->
dev
,
spi
,
0x40
|
(
addr
<<
1
),
type
,
pdata
,
addr
);
0x40
|
(
addr
<<
1
),
type
,
pdata
->
base
,
addr
);
if
(
status
<
0
)
goto
fail
;
return
status
;
if
(
pdata
->
base
!=
-
1
)
pdata
->
base
+=
data
->
mcp
[
addr
]
->
chip
.
ngpio
;
...
...
@@ -943,36 +1121,6 @@ static int mcp23s08_probe(struct spi_device *spi)
}
data
->
ngpio
=
ngpio
;
/* NOTE: these chips have a relatively sane IRQ framework, with
* per-signal masking and level/edge triggering. It's not yet
* handled here...
*/
return
0
;
fail:
for
(
addr
=
0
;
addr
<
ARRAY_SIZE
(
data
->
mcp
);
addr
++
)
{
if
(
!
data
->
mcp
[
addr
])
continue
;
gpiochip_remove
(
&
data
->
mcp
[
addr
]
->
chip
);
}
return
status
;
}
static
int
mcp23s08_remove
(
struct
spi_device
*
spi
)
{
struct
mcp23s08_driver_data
*
data
=
spi_get_drvdata
(
spi
);
unsigned
addr
;
for
(
addr
=
0
;
addr
<
ARRAY_SIZE
(
data
->
mcp
);
addr
++
)
{
if
(
!
data
->
mcp
[
addr
])
continue
;
gpiochip_remove
(
&
data
->
mcp
[
addr
]
->
chip
);
}
return
0
;
}
...
...
@@ -986,7 +1134,6 @@ MODULE_DEVICE_TABLE(spi, mcp23s08_ids);
static
struct
spi_driver
mcp23s08_driver
=
{
.
probe
=
mcp23s08_probe
,
.
remove
=
mcp23s08_remove
,
.
id_table
=
mcp23s08_ids
,
.
driver
=
{
.
name
=
"mcp23s08"
,
...
...
include/linux/spi/mcp23s08.h
浏览文件 @
350fe5ee
/* FIXME driver should be able to handle IRQs... */
struct
mcp23s08_chip_info
{
bool
is_present
;
/* true if populated */
unsigned
pullups
;
/* BIT(x) means enable pullup x */
};
struct
mcp23s08_platform_data
{
/* For mcp23s08, up to 4 slaves (numbered 0..3) can share one SPI
* chipselect, each providing 1 gpio_chip instance with 8 gpios.
...
...
@@ -13,31 +5,13 @@ struct mcp23s08_platform_data {
* chipselect, each providing 1 gpio_chip (port A + port B) with
* 16 gpios.
*/
struct
mcp23s08_chip_info
chip
[
8
]
;
u32
spi_present_mask
;
/* "base" is the number of the first GPIO
. Dynamic assignment is
*
not currently supported, and even if there are gaps in chip
*
addressing the GPIO numbers are sequential .. so for example
*
if only slaves 0 and 3 are present, their GPIOs range from
*
base to base+15
(or base+31 for s17 variant).
/* "base" is the number of the first GPIO
or -1 for dynamic
*
assignment. If there are gaps in chip addressing the GPIO
*
numbers are sequential .. so for example if only slaves 0
*
and 3 are present, their GPIOs range from base to base+15
* (or base+31 for s17 variant).
*/
unsigned
base
;
/* Marks the device as a interrupt controller.
* NOTE: The interrupt functionality is only supported for i2c
* versions of the chips. The spi chips can also do the interrupts,
* but this is not supported by the linux driver yet.
*/
bool
irq_controller
;
/* Sets the mirror flag in the IOCON register. Devices
* with two interrupt outputs (these are the devices ending with 17 and
* those that have 16 IOs) have two IO banks: IO 0-7 form bank 1 and
* IO 8-15 are bank 2. These chips have two different interrupt outputs:
* One for bank 1 and another for bank 2. If irq-mirror is set, both
* interrupts are generated regardless of the bank that an input change
* occurred on. If it is not set, the interrupt are only generated for
* the bank they belong to.
* On devices with only one interrupt output this property is useless.
*/
bool
mirror
;
};
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录