Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
L
linux
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
linux
Commits
4672a4a9
Commit
4672a4a9
authored
Jul 09, 2020
by
Linus Walleij
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'devel' into for-next
parents
93e0272a
a6175e89
Changes
29
Hide whitespace changes
Inline
Side-by-side
Showing
29 changed files
with
1367 additions
and
1214 deletions
+1367
-1214
Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
+1
-0
Documentation/devicetree/bindings/gpio/gpio-zynq.txt
Documentation/devicetree/bindings/gpio/gpio-zynq.txt
+3
-1
Documentation/driver-api/gpio/drivers-on-gpio.rst
Documentation/driver-api/gpio/drivers-on-gpio.rst
+7
-0
arch/arm/plat-orion/gpio.c
arch/arm/plat-orion/gpio.c
+2
-6
drivers/gpio/Kconfig
drivers/gpio/Kconfig
+2
-2
drivers/gpio/Makefile
drivers/gpio/Makefile
+1
-0
drivers/gpio/TODO
drivers/gpio/TODO
+1
-1
drivers/gpio/gpio-aggregator.c
drivers/gpio/gpio-aggregator.c
+28
-35
drivers/gpio/gpio-altera.c
drivers/gpio/gpio-altera.c
+2
-1
drivers/gpio/gpio-it87.c
drivers/gpio/gpio-it87.c
+7
-7
drivers/gpio/gpio-max732x.c
drivers/gpio/gpio-max732x.c
+1
-1
drivers/gpio/gpio-mlxbf.c
drivers/gpio/gpio-mlxbf.c
+1
-1
drivers/gpio/gpio-mlxbf2.c
drivers/gpio/gpio-mlxbf2.c
+3
-1
drivers/gpio/gpio-mpc8xxx.c
drivers/gpio/gpio-mpc8xxx.c
+1
-1
drivers/gpio/gpio-mvebu.c
drivers/gpio/gpio-mvebu.c
+2
-6
drivers/gpio/gpio-omap.c
drivers/gpio/gpio-omap.c
+26
-0
drivers/gpio/gpio-pca953x.c
drivers/gpio/gpio-pca953x.c
+2
-0
drivers/gpio/gpio-pmic-eic-sprd.c
drivers/gpio/gpio-pmic-eic-sprd.c
+1
-1
drivers/gpio/gpio-sama5d2-piobu.c
drivers/gpio/gpio-sama5d2-piobu.c
+8
-8
drivers/gpio/gpio-syscon.c
drivers/gpio/gpio-syscon.c
+6
-6
drivers/gpio/gpio-xra1403.c
drivers/gpio/gpio-xra1403.c
+2
-6
drivers/gpio/gpio-zynq.c
drivers/gpio/gpio-zynq.c
+64
-2
drivers/gpio/gpiolib-cdev.c
drivers/gpio/gpiolib-cdev.c
+1154
-0
drivers/gpio/gpiolib-cdev.h
drivers/gpio/gpiolib-cdev.h
+11
-0
drivers/gpio/gpiolib-of.c
drivers/gpio/gpiolib-of.c
+3
-0
drivers/gpio/gpiolib-sysfs.c
drivers/gpio/gpiolib-sysfs.c
+1
-1
drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.c
+8
-1121
drivers/pinctrl/pinctrl-at91.c
drivers/pinctrl/pinctrl-at91.c
+2
-5
include/linux/gpio/driver.h
include/linux/gpio/driver.h
+17
-1
No files found.
Documentation/devicetree/bindings/gpio/gpio-pca953x.txt
View file @
4672a4a9
...
...
@@ -19,6 +19,7 @@ Required properties:
nxp,pca9698
nxp,pcal6416
nxp,pcal6524
nxp,pcal9535
nxp,pcal9555a
maxim,max7310
maxim,max7312
...
...
Documentation/devicetree/bindings/gpio/gpio-zynq.txt
View file @
4672a4a9
...
...
@@ -6,7 +6,9 @@ Required properties:
- First cell is the GPIO line number
- Second cell is used to specify optional
parameters (unused)
- compatible : Should be "xlnx,zynq-gpio-1.0" or "xlnx,zynqmp-gpio-1.0"
- compatible : Should be "xlnx,zynq-gpio-1.0" or
"xlnx,zynqmp-gpio-1.0" or "xlnx,versal-gpio-1.0
or "xlnx,pmc-gpio-1.0
- clocks : Clock specifier (see clock bindings for details)
- gpio-controller : Marks the device node as a GPIO controller.
- interrupts : Interrupt specifier (see interrupt bindings for
...
...
Documentation/driver-api/gpio/drivers-on-gpio.rst
View file @
4672a4a9
...
...
@@ -89,6 +89,13 @@ hardware descriptions such as device tree or ACPI:
Consumer Electronics Control bus using only GPIO. It is used to communicate
with devices on the HDMI bus.
- gpio-charger: drivers/power/supply/gpio-charger.c is used if you need to do
battery charging and all you have to go by to check the presence of the
AC charger or more complex tasks such as indicating charging status using
nothing but GPIO lines, this driver provides that and also a clearly defined
way to pass the charging parameters from hardware descriptions such as the
device tree.
Apart from this there are special GPIO drivers in subsystems like MMC/SD to
read card detect and write protect GPIO lines, and in the TTY serial subsystem
to emulate MCTRL (modem control) signals CTS/RTS by using two GPIO lines. The
...
...
arch/arm/plat-orion/gpio.c
View file @
4672a4a9
...
...
@@ -442,6 +442,7 @@ static void orion_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
struct
orion_gpio_chip
*
ochip
=
gpiochip_get_data
(
chip
);
u32
out
,
io_conf
,
blink
,
in_pol
,
data_in
,
cause
,
edg_msk
,
lvl_msk
;
const
char
*
label
;
int
i
;
out
=
readl_relaxed
(
GPIO_OUT
(
ochip
));
...
...
@@ -453,15 +454,10 @@ static void orion_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
edg_msk
=
readl_relaxed
(
GPIO_EDGE_MASK
(
ochip
));
lvl_msk
=
readl_relaxed
(
GPIO_LEVEL_MASK
(
ochip
));
for
(
i
=
0
;
i
<
chip
->
ngpio
;
i
++
)
{
const
char
*
label
;
for_each_requested_gpio
(
chip
,
i
,
label
)
{
u32
msk
;
bool
is_out
;
label
=
gpiochip_is_requested
(
chip
,
i
);
if
(
!
label
)
continue
;
msk
=
1
<<
i
;
is_out
=
!
(
io_conf
&
msk
);
...
...
drivers/gpio/Kconfig
View file @
4672a4a9
...
...
@@ -410,7 +410,7 @@ config GPIO_MXS
config GPIO_OCTEON
tristate "Cavium OCTEON GPIO"
depends on
GPIOLIB &&
CAVIUM_OCTEON_SOC
depends on CAVIUM_OCTEON_SOC
default y
help
Say yes here to support the on-chip GPIO lines on the OCTEON
...
...
@@ -1117,7 +1117,7 @@ config GPIO_DLN2
config HTC_EGPIO
bool "HTC EGPIO support"
depends on
GPIOLIB &&
ARM
depends on ARM
help
This driver supports the CPLD egpio chip present on
several HTC phones. It provides basic support for input
...
...
drivers/gpio/Makefile
View file @
4672a4a9
...
...
@@ -7,6 +7,7 @@ obj-$(CONFIG_GPIOLIB) += gpiolib.o
obj-$(CONFIG_GPIOLIB)
+=
gpiolib-devres.o
obj-$(CONFIG_GPIOLIB)
+=
gpiolib-legacy.o
obj-$(CONFIG_GPIOLIB)
+=
gpiolib-devprop.o
obj-$(CONFIG_GPIOLIB)
+=
gpiolib-cdev.o
obj-$(CONFIG_OF_GPIO)
+=
gpiolib-of.o
obj-$(CONFIG_GPIO_SYSFS)
+=
gpiolib-sysfs.o
obj-$(CONFIG_GPIO_ACPI)
+=
gpiolib-acpi.o
...
...
drivers/gpio/TODO
View file @
4672a4a9
...
...
@@ -5,7 +5,7 @@ subsystem.
GPIO descriptors
Starting with commit 79a9becda894 the GPIO subsystem embarked on a journey
to move away from the global GPIO numberspace and toward a decriptor-based
to move away from the global GPIO numberspace and toward a de
s
criptor-based
approach. This means that GPIO consumers, drivers and machine descriptions
ideally have no use or idea of the global GPIO numberspace that has/was
used in the inception of the GPIO subsystem.
...
...
drivers/gpio/gpio-aggregator.c
View file @
4672a4a9
...
...
@@ -10,6 +10,7 @@
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/ctype.h>
#include <linux/gpio.h>
#include <linux/gpio/consumer.h>
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
...
...
@@ -38,9 +39,9 @@ static DEFINE_IDR(gpio_aggregator_idr);
static
char
*
get_arg
(
char
**
args
)
{
char
*
start
=
*
args
,
*
end
;
char
*
start
,
*
end
;
start
=
skip_spaces
(
start
);
start
=
skip_spaces
(
*
args
);
if
(
!*
start
)
return
NULL
;
...
...
@@ -111,55 +112,45 @@ static int aggr_add_gpio(struct gpio_aggregator *aggr, const char *key,
static
int
aggr_parse
(
struct
gpio_aggregator
*
aggr
)
{
unsigned
int
first_index
,
last_index
,
i
,
n
=
0
;
char
*
name
,
*
offsets
,
*
first
,
*
last
,
*
next
;
char
*
args
=
aggr
->
args
;
int
error
;
unsigned
long
*
bitmap
;
unsigned
int
i
,
n
=
0
;
char
*
name
,
*
offsets
;
int
error
=
0
;
bitmap
=
bitmap_alloc
(
ARCH_NR_GPIOS
,
GFP_KERNEL
);
if
(
!
bitmap
)
return
-
ENOMEM
;
for
(
name
=
get_arg
(
&
args
),
offsets
=
get_arg
(
&
args
);
name
;
offsets
=
get_arg
(
&
args
))
{
if
(
IS_ERR
(
name
))
{
pr_err
(
"Cannot get GPIO specifier: %pe
\n
"
,
name
);
return
PTR_ERR
(
name
);
error
=
PTR_ERR
(
name
);
goto
free_bitmap
;
}
if
(
!
isrange
(
offsets
))
{
/* Named GPIO line */
error
=
aggr_add_gpio
(
aggr
,
name
,
U16_MAX
,
&
n
);
if
(
error
)
return
error
;
goto
free_bitmap
;
name
=
offsets
;
continue
;
}
/* GPIO chip + offset(s) */
for
(
first
=
offsets
;
*
first
;
first
=
next
)
{
next
=
strchrnul
(
first
,
','
);
if
(
*
next
)
*
next
++
=
'\0'
;
last
=
strchr
(
first
,
'-'
);
if
(
last
)
*
last
++
=
'\0'
;
if
(
kstrtouint
(
first
,
10
,
&
first_index
))
{
pr_err
(
"Cannot parse GPIO index %s
\n
"
,
first
);
return
-
EINVAL
;
}
if
(
!
last
)
{
last_index
=
first_index
;
}
else
if
(
kstrtouint
(
last
,
10
,
&
last_index
))
{
pr_err
(
"Cannot parse GPIO index %s
\n
"
,
last
);
return
-
EINVAL
;
}
for
(
i
=
first_index
;
i
<=
last_index
;
i
++
)
{
error
=
aggr_add_gpio
(
aggr
,
name
,
i
,
&
n
);
if
(
error
)
return
error
;
}
error
=
bitmap_parselist
(
offsets
,
bitmap
,
ARCH_NR_GPIOS
);
if
(
error
)
{
pr_err
(
"Cannot parse %s: %d
\n
"
,
offsets
,
error
);
goto
free_bitmap
;
}
for_each_set_bit
(
i
,
bitmap
,
ARCH_NR_GPIOS
)
{
error
=
aggr_add_gpio
(
aggr
,
name
,
i
,
&
n
);
if
(
error
)
goto
free_bitmap
;
}
name
=
get_arg
(
&
args
);
...
...
@@ -167,10 +158,12 @@ static int aggr_parse(struct gpio_aggregator *aggr)
if
(
!
n
)
{
pr_err
(
"No GPIOs specified
\n
"
);
return
-
EINVAL
;
error
=
-
EINVAL
;
}
return
0
;
free_bitmap:
bitmap_free
(
bitmap
);
return
error
;
}
static
ssize_t
new_device_store
(
struct
device_driver
*
driver
,
const
char
*
buf
,
...
...
drivers/gpio/gpio-altera.c
View file @
4672a4a9
...
...
@@ -24,6 +24,7 @@
* @interrupt_trigger : specifies the hardware configured IRQ trigger type
* (rising, falling, both, high)
* @mapped_irq : kernel mapped irq number.
* @irq_chip : IRQ chip configuration
*/
struct
altera_gpio_chip
{
struct
of_mm_gpio_chip
mmchip
;
...
...
@@ -69,7 +70,7 @@ static void altera_gpio_irq_mask(struct irq_data *d)
raw_spin_unlock_irqrestore
(
&
altera_gc
->
gpio_lock
,
flags
);
}
/*
*
/*
* This controller's IRQ type is synthesized in hardware, so this function
* just checks if the requested set_type matches the synthesized IRQ type
*/
...
...
drivers/gpio/gpio-it87.c
View file @
4672a4a9
...
...
@@ -47,13 +47,13 @@
/**
* struct it87_gpio - it87-specific GPIO chip
* @chip the underlying gpio_chip structure
* @lock a lock to avoid races between operations
* @io_base base address for gpio ports
* @io_size size of the port rage starting from io_base.
* @output_base Super I/O register address for Output Enable register
* @simple_base Super I/O 'Simple I/O' Enable register
* @simple_size Super IO 'Simple I/O' Enable register size; this is
* @chip
:
the underlying gpio_chip structure
* @lock
:
a lock to avoid races between operations
* @io_base
:
base address for gpio ports
* @io_size
:
size of the port rage starting from io_base.
* @output_base
:
Super I/O register address for Output Enable register
* @simple_base
:
Super I/O 'Simple I/O' Enable register
* @simple_size
:
Super IO 'Simple I/O' Enable register size; this is
* required because IT87xx chips might only provide Simple I/O
* switches on a subset of lines, whereas the others keep the
* same status all time.
...
...
drivers/gpio/gpio-max732x.c
View file @
4672a4a9
...
...
@@ -703,7 +703,7 @@ static int max732x_probe(struct i2c_client *client,
if
(
ret
)
return
ret
;
if
(
pdata
&&
pdata
->
setup
)
{
if
(
pdata
->
setup
)
{
ret
=
pdata
->
setup
(
client
,
chip
->
gpio_chip
.
base
,
chip
->
gpio_chip
.
ngpio
,
pdata
->
context
);
if
(
ret
<
0
)
...
...
drivers/gpio/gpio-mlxbf.c
View file @
4672a4a9
...
...
@@ -127,7 +127,7 @@ static int mlxbf_gpio_resume(struct platform_device *pdev)
}
#endif
static
const
struct
acpi_device_id
mlxbf_gpio_acpi_match
[]
=
{
static
const
struct
acpi_device_id
__maybe_unused
mlxbf_gpio_acpi_match
[]
=
{
{
"MLNXBF02"
,
0
},
{}
};
...
...
drivers/gpio/gpio-mlxbf2.c
View file @
4672a4a9
...
...
@@ -149,6 +149,8 @@ static int mlxbf2_gpio_lock_acquire(struct mlxbf2_gpio_context *gs)
* Release the YU arm_gpio_lock after changing the direction mode.
*/
static
void
mlxbf2_gpio_lock_release
(
struct
mlxbf2_gpio_context
*
gs
)
__releases
(
&
gs
->
gc
.
bgpio_lock
)
__releases
(
yu_arm_gpio_lock_param
.
lock
)
{
writel
(
YU_ARM_GPIO_LOCK_RELEASE
,
yu_arm_gpio_lock_param
.
io
);
spin_unlock
(
&
gs
->
gc
.
bgpio_lock
);
...
...
@@ -309,7 +311,7 @@ static int mlxbf2_gpio_resume(struct platform_device *pdev)
}
#endif
static
const
struct
acpi_device_id
mlxbf2_gpio_acpi_match
[]
=
{
static
const
struct
acpi_device_id
__maybe_unused
mlxbf2_gpio_acpi_match
[]
=
{
{
"MLNXBF22"
,
0
},
{},
};
...
...
drivers/gpio/gpio-mpc8xxx.c
View file @
4672a4a9
...
...
@@ -417,7 +417,7 @@ static int mpc8xxx_probe(struct platform_device *pdev)
ret
=
devm_request_irq
(
&
pdev
->
dev
,
mpc8xxx_gc
->
irqn
,
mpc8xxx_gpio_irq_cascade
,
IRQF_
NO_THREAD
|
IRQF_
SHARED
,
"gpio-cascade"
,
IRQF_SHARED
,
"gpio-cascade"
,
mpc8xxx_gc
);
if
(
ret
)
{
dev_err
(
&
pdev
->
dev
,
"%s: failed to devm_request_irq(%d), ret = %d
\n
"
,
...
...
drivers/gpio/gpio-mvebu.c
View file @
4672a4a9
...
...
@@ -846,6 +846,7 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
{
struct
mvebu_gpio_chip
*
mvchip
=
gpiochip_get_data
(
chip
);
u32
out
,
io_conf
,
blink
,
in_pol
,
data_in
,
cause
,
edg_msk
,
lvl_msk
;
const
char
*
label
;
int
i
;
regmap_read
(
mvchip
->
regs
,
GPIO_OUT_OFF
+
mvchip
->
offset
,
&
out
);
...
...
@@ -857,15 +858,10 @@ static void mvebu_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
edg_msk
=
mvebu_gpio_read_edge_mask
(
mvchip
);
lvl_msk
=
mvebu_gpio_read_level_mask
(
mvchip
);
for
(
i
=
0
;
i
<
chip
->
ngpio
;
i
++
)
{
const
char
*
label
;
for_each_requested_gpio
(
chip
,
i
,
label
)
{
u32
msk
;
bool
is_out
;
label
=
gpiochip_is_requested
(
chip
,
i
);
if
(
!
label
)
continue
;
msk
=
BIT
(
i
);
is_out
=
!
(
io_conf
&
msk
);
...
...
drivers/gpio/gpio-omap.c
View file @
4672a4a9
...
...
@@ -60,6 +60,7 @@ struct gpio_bank {
struct
clk
*
dbck
;
struct
notifier_block
nb
;
unsigned
int
is_suspended
:
1
;
unsigned
int
needs_resume
:
1
;
u32
mod_usage
;
u32
irq_usage
;
u32
dbck_enable_mask
;
...
...
@@ -1504,9 +1505,34 @@ static int __maybe_unused omap_gpio_runtime_resume(struct device *dev)
return
0
;
}
static
int
omap_gpio_suspend
(
struct
device
*
dev
)
{
struct
gpio_bank
*
bank
=
dev_get_drvdata
(
dev
);
if
(
bank
->
is_suspended
)
return
0
;
bank
->
needs_resume
=
1
;
return
omap_gpio_runtime_suspend
(
dev
);
}
static
int
omap_gpio_resume
(
struct
device
*
dev
)
{
struct
gpio_bank
*
bank
=
dev_get_drvdata
(
dev
);
if
(
!
bank
->
needs_resume
)
return
0
;
bank
->
needs_resume
=
0
;
return
omap_gpio_runtime_resume
(
dev
);
}
static
const
struct
dev_pm_ops
gpio_pm_ops
=
{
SET_RUNTIME_PM_OPS
(
omap_gpio_runtime_suspend
,
omap_gpio_runtime_resume
,
NULL
)
SET_LATE_SYSTEM_SLEEP_PM_OPS
(
omap_gpio_suspend
,
omap_gpio_resume
)
};
static
struct
platform_driver
omap_gpio_driver
=
{
...
...
drivers/gpio/gpio-pca953x.c
View file @
4672a4a9
...
...
@@ -89,6 +89,7 @@ static const struct i2c_device_id pca953x_id[] = {
{
"pcal6416"
,
16
|
PCA953X_TYPE
|
PCA_LATCH_INT
,
},
{
"pcal6524"
,
24
|
PCA953X_TYPE
|
PCA_LATCH_INT
,
},
{
"pcal9535"
,
16
|
PCA953X_TYPE
|
PCA_LATCH_INT
,
},
{
"pcal9555a"
,
16
|
PCA953X_TYPE
|
PCA_LATCH_INT
,
},
{
"max7310"
,
8
|
PCA953X_TYPE
,
},
...
...
@@ -1234,6 +1235,7 @@ static const struct of_device_id pca953x_dt_ids[] = {
{
.
compatible
=
"nxp,pcal6416"
,
.
data
=
OF_953X
(
16
,
PCA_LATCH_INT
),
},
{
.
compatible
=
"nxp,pcal6524"
,
.
data
=
OF_953X
(
24
,
PCA_LATCH_INT
),
},
{
.
compatible
=
"nxp,pcal9535"
,
.
data
=
OF_953X
(
16
,
PCA_LATCH_INT
),
},
{
.
compatible
=
"nxp,pcal9555a"
,
.
data
=
OF_953X
(
16
,
PCA_LATCH_INT
),
},
{
.
compatible
=
"maxim,max7310"
,
.
data
=
OF_953X
(
8
,
0
),
},
...
...
drivers/gpio/gpio-pmic-eic-sprd.c
View file @
4672a4a9
...
...
@@ -48,7 +48,7 @@ enum {
* struct sprd_pmic_eic - PMIC EIC controller
* @chip: the gpio_chip structure.
* @intc: the irq_chip structure.
* @
regmap:
the regmap from the parent device.
* @
map:
the regmap from the parent device.
* @offset: the EIC controller's offset address of the PMIC.
* @reg: the array to cache the EIC registers.
* @buslock: for bus lock/sync and unlock.
...
...
drivers/gpio/gpio-sama5d2-piobu.c
View file @
4672a4a9
...
...
@@ -49,7 +49,7 @@ struct sama5d2_piobu {
struct
regmap
*
regmap
;
};
/*
*
/*
* sama5d2_piobu_setup_pin() - prepares a pin for set_direction call
*
* Do not consider pin for tamper detection (normal and backup modes)
...
...
@@ -73,7 +73,7 @@ static int sama5d2_piobu_setup_pin(struct gpio_chip *chip, unsigned int pin)
return
regmap_update_bits
(
piobu
->
regmap
,
PIOBU_WKPR
,
mask
,
0
);
}
/*
*
/*
* sama5d2_piobu_write_value() - writes value & mask at the pin's PIOBU register
*/
static
int
sama5d2_piobu_write_value
(
struct
gpio_chip
*
chip
,
unsigned
int
pin
,
...
...
@@ -88,7 +88,7 @@ static int sama5d2_piobu_write_value(struct gpio_chip *chip, unsigned int pin,
return
regmap_update_bits
(
piobu
->
regmap
,
reg
,
mask
,
value
);
}
/*
*
/*
* sama5d2_piobu_read_value() - read the value with masking from the pin's PIOBU
* register
*/
...
...
@@ -108,7 +108,7 @@ static int sama5d2_piobu_read_value(struct gpio_chip *chip, unsigned int pin,
return
val
&
mask
;
}
/*
*
/*
* sama5d2_piobu_get_direction() - gpiochip get_direction
*/
static
int
sama5d2_piobu_get_direction
(
struct
gpio_chip
*
chip
,
...
...
@@ -123,7 +123,7 @@ static int sama5d2_piobu_get_direction(struct gpio_chip *chip,
GPIO_LINE_DIRECTION_OUT
;
}
/*
*
/*
* sama5d2_piobu_direction_input() - gpiochip direction_input
*/
static
int
sama5d2_piobu_direction_input
(
struct
gpio_chip
*
chip
,
...
...
@@ -132,7 +132,7 @@ static int sama5d2_piobu_direction_input(struct gpio_chip *chip,
return
sama5d2_piobu_write_value
(
chip
,
pin
,
PIOBU_DIRECTION
,
PIOBU_IN
);
}
/*
*
/*
* sama5d2_piobu_direction_output() - gpiochip direction_output
*/
static
int
sama5d2_piobu_direction_output
(
struct
gpio_chip
*
chip
,
...
...
@@ -147,7 +147,7 @@ static int sama5d2_piobu_direction_output(struct gpio_chip *chip,
val
);
}
/*
*
/*
* sama5d2_piobu_get() - gpiochip get
*/
static
int
sama5d2_piobu_get
(
struct
gpio_chip
*
chip
,
unsigned
int
pin
)
...
...
@@ -166,7 +166,7 @@ static int sama5d2_piobu_get(struct gpio_chip *chip, unsigned int pin)
return
!!
ret
;
}
/*
*
/*
* sama5d2_piobu_set() - gpiochip set
*/
static
void
sama5d2_piobu_set
(
struct
gpio_chip
*
chip
,
unsigned
int
pin
,
...
...
drivers/gpio/gpio-syscon.c
View file @
4672a4a9
...
...
@@ -24,16 +24,16 @@
/**
* struct syscon_gpio_data - Configuration for the device.
* compatible: SYSCON driver compatible string.
* flags: Set of GPIO_SYSCON_FEAT_ flags:
*
@
compatible: SYSCON driver compatible string.
*
@
flags: Set of GPIO_SYSCON_FEAT_ flags:
* GPIO_SYSCON_FEAT_IN: GPIOs supports input,
* GPIO_SYSCON_FEAT_OUT: GPIOs supports output,
* GPIO_SYSCON_FEAT_DIR: GPIOs supports switch direction.
* bit_count: Number of bits used as GPIOs.
* dat_bit_offset: Offset (in bits) to the first GPIO bit.
* dir_bit_offset: Optional offset (in bits) to the first bit to switch
*
@
bit_count: Number of bits used as GPIOs.
*
@
dat_bit_offset: Offset (in bits) to the first GPIO bit.
*
@
dir_bit_offset: Optional offset (in bits) to the first bit to switch
* GPIO direction (Used with GPIO_SYSCON_FEAT_DIR flag).
* set: HW specific callback to assigns output value
*
@
set: HW specific callback to assigns output value
* for signal "offset"
*/
...
...
drivers/gpio/gpio-xra1403.c
View file @
4672a4a9
...
...
@@ -121,6 +121,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip)
struct
xra1403
*
xra
=
gpiochip_get_data
(
chip
);
int
value
[
XRA_LAST
];
int
i
;
const
char
*
label
;
unsigned
int
gcr
;
unsigned
int
gsr
;
...
...
@@ -136,12 +137,7 @@ static void xra1403_dbg_show(struct seq_file *s, struct gpio_chip *chip)
gcr
=
value
[
XRA_GCR
+
1
]
<<
8
|
value
[
XRA_GCR
];
gsr
=
value
[
XRA_GSR
+
1
]
<<
8
|
value
[
XRA_GSR
];
for
(
i
=
0
;
i
<
chip
->
ngpio
;
i
++
)
{
const
char
*
label
=
gpiochip_is_requested
(
chip
,
i
);
if
(
!
label
)
continue
;
for_each_requested_gpio
(
chip
,
i
,
label
)
{
seq_printf
(
s
,
" gpio-%-3d (%-12s) %s %s
\n
"
,
chip
->
base
+
i
,
label
,
(
gcr
&
BIT
(
i
))
?
"in"
:
"out"
,
...
...
drivers/gpio/gpio-zynq.c
View file @
4672a4a9
...
...
@@ -10,6 +10,7 @@
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/module.h>
#include <linux/platform_device.h>
...
...
@@ -21,6 +22,9 @@
/* Maximum banks */
#define ZYNQ_GPIO_MAX_BANK 4
#define ZYNQMP_GPIO_MAX_BANK 6
#define VERSAL_GPIO_MAX_BANK 4
#define PMC_GPIO_MAX_BANK 5
#define VERSAL_UNUSED_BANKS 2
#define ZYNQ_GPIO_BANK0_NGPIO 32
#define ZYNQ_GPIO_BANK1_NGPIO 22
...
...
@@ -95,6 +99,7 @@
/* set to differentiate zynq from zynqmp, 0=zynqmp, 1=zynq */
#define ZYNQ_GPIO_QUIRK_IS_ZYNQ BIT(0)
#define GPIO_QUIRK_DATA_RO_BUG BIT(1)
#define GPIO_QUIRK_VERSAL BIT(2)
struct
gpio_regs
{
u32
datamsw
[
ZYNQMP_GPIO_MAX_BANK
];
...
...
@@ -116,6 +121,7 @@ struct gpio_regs {
* @irq: interrupt for the GPIO device
* @p_data: pointer to platform data
* @context: context registers
* @dirlock: lock used for direction in/out synchronization
*/
struct
zynq_gpio
{
struct
gpio_chip
chip
;
...
...
@@ -124,6 +130,7 @@ struct zynq_gpio {
int
irq
;
const
struct
zynq_platform_data
*
p_data
;
struct
gpio_regs
context
;
spinlock_t
dirlock
;
/* lock */
};
/**
...
...
@@ -196,6 +203,8 @@ static inline void zynq_gpio_get_bank_pin(unsigned int pin_num,
gpio
->
p_data
->
bank_min
[
bank
];
return
;
}
if
(
gpio
->
p_data
->
quirks
&
GPIO_QUIRK_VERSAL
)
bank
=
bank
+
VERSAL_UNUSED_BANKS
;
}
/* default */
...
...
@@ -297,6 +306,7 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
{
u32
reg
;
unsigned
int
bank_num
,
bank_pin_num
;
unsigned
long
flags
;
struct
zynq_gpio
*
gpio
=
gpiochip_get_data
(
chip
);
zynq_gpio_get_bank_pin
(
pin
,
&
bank_num
,
&
bank_pin_num
,
gpio
);
...
...
@@ -310,9 +320,11 @@ static int zynq_gpio_dir_in(struct gpio_chip *chip, unsigned int pin)
return
-
EINVAL
;
/* clear the bit in direction mode reg to set the pin as input */
spin_lock_irqsave
(
&
gpio
->
dirlock
,
flags
);
reg
=
readl_relaxed
(
gpio
->
base_addr
+
ZYNQ_GPIO_DIRM_OFFSET
(
bank_num
));
reg
&=
~
BIT
(
bank_pin_num
);
writel_relaxed
(
reg
,
gpio
->
base_addr
+
ZYNQ_GPIO_DIRM_OFFSET
(
bank_num
));
spin_unlock_irqrestore
(
&
gpio
->
dirlock
,
flags
);
return
0
;
}
...
...
@@ -334,11 +346,13 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
{
u32
reg
;
unsigned
int
bank_num
,
bank_pin_num
;
unsigned
long
flags
;
struct
zynq_gpio
*
gpio
=
gpiochip_get_data
(
chip
);
zynq_gpio_get_bank_pin
(
pin
,
&
bank_num
,
&
bank_pin_num
,
gpio
);
/* set the GPIO pin as output */
spin_lock_irqsave
(
&
gpio
->
dirlock
,
flags
);
reg
=
readl_relaxed
(
gpio
->
base_addr
+
ZYNQ_GPIO_DIRM_OFFSET
(
bank_num
));
reg
|=
BIT
(
bank_pin_num
);
writel_relaxed
(
reg
,
gpio
->
base_addr
+
ZYNQ_GPIO_DIRM_OFFSET
(
bank_num
));
...
...
@@ -347,6 +361,7 @@ static int zynq_gpio_dir_out(struct gpio_chip *chip, unsigned int pin,
reg
=
readl_relaxed
(
gpio
->
base_addr
+
ZYNQ_GPIO_OUTEN_OFFSET
(
bank_num
));
reg
|=
BIT
(
bank_pin_num
);
writel_relaxed
(
reg
,
gpio
->
base_addr
+
ZYNQ_GPIO_OUTEN_OFFSET
(
bank_num
));
spin_unlock_irqrestore
(
&
gpio
->
dirlock
,
flags
);
/* set the state of the pin */
zynq_gpio_set_value
(
chip
,
pin
,
state
);
...
...
@@ -647,6 +662,8 @@ static void zynq_gpio_irqhandler(struct irq_desc *desc)
int_enb
=
readl_relaxed
(
gpio
->
base_addr
+
ZYNQ_GPIO_INTMASK_OFFSET
(
bank_num
));
zynq_gpio_handle_bank_irq
(
gpio
,
bank_num
,
int_sts
&
~
int_enb
);
if
(
gpio
->
p_data
->
quirks
&
GPIO_QUIRK_VERSAL
)
bank_num
=
bank_num
+
VERSAL_UNUSED_BANKS
;
}
chained_irq_exit
(
irqchip
,
desc
);
...
...
@@ -676,6 +693,8 @@ static void zynq_gpio_save_context(struct zynq_gpio *gpio)
gpio
->
context
.
int_any
[
bank_num
]
=
readl_relaxed
(
gpio
->
base_addr
+
ZYNQ_GPIO_INTANY_OFFSET
(
bank_num
));
if
(
gpio
->
p_data
->
quirks
&
GPIO_QUIRK_VERSAL
)
bank_num
=
bank_num
+
VERSAL_UNUSED_BANKS
;
}
}
...
...
@@ -707,6 +726,8 @@ static void zynq_gpio_restore_context(struct zynq_gpio *gpio)
writel_relaxed
(
~
(
gpio
->
context
.
int_en
[
bank_num
]),
gpio
->
base_addr
+
ZYNQ_GPIO_INTEN_OFFSET
(
bank_num
));
if
(
gpio
->
p_data
->
quirks
&
GPIO_QUIRK_VERSAL
)
bank_num
=
bank_num
+
VERSAL_UNUSED_BANKS
;
}
}
...
...
@@ -715,6 +736,9 @@ static int __maybe_unused zynq_gpio_suspend(struct device *dev)
struct
zynq_gpio
*
gpio
=
dev_get_drvdata
(
dev
);
struct
irq_data
*
data
=
irq_get_irq_data
(
gpio
->
irq
);
if
(
!
device_may_wakeup
(
dev
))
disable_irq
(
gpio
->
irq
);
if
(
!
irqd_is_wakeup_set
(
data
))
{
zynq_gpio_save_context
(
gpio
);
return
pm_runtime_force_suspend
(
dev
);
...
...
@@ -729,6 +753,9 @@ static int __maybe_unused zynq_gpio_resume(struct device *dev)
struct
irq_data
*
data
=
irq_get_irq_data
(
gpio
->
irq
);
int
ret
;
if
(
!
device_may_wakeup
(
dev
))
enable_irq
(
gpio
->
irq
);
if
(
!
irqd_is_wakeup_set
(
data
))
{
ret
=
pm_runtime_force_resume
(
dev
);
zynq_gpio_restore_context
(
gpio
);
...
...
@@ -778,6 +805,31 @@ static const struct dev_pm_ops zynq_gpio_dev_pm_ops = {
zynq_gpio_runtime_resume
,
NULL
)
};
static
const
struct
zynq_platform_data
versal_gpio_def
=
{
.
label
=
"versal_gpio"
,
.
quirks
=
GPIO_QUIRK_VERSAL
,
.
ngpio
=
58
,
.
max_bank
=
VERSAL_GPIO_MAX_BANK
,
.
bank_min
[
0
]
=
0
,
.
bank_max
[
0
]
=
25
,
/* 0 to 25 are connected to MIOs (26 pins) */
.
bank_min
[
3
]
=
26
,
.
bank_max
[
3
]
=
57
,
/* Bank 3 is connected to FMIOs (32 pins) */
};
static
const
struct
zynq_platform_data
pmc_gpio_def
=
{
.
label
=
"pmc_gpio"
,
.
ngpio
=
116
,
.
max_bank
=
PMC_GPIO_MAX_BANK
,
.
bank_min
[
0
]
=
0
,
.
bank_max
[
0
]
=
25
,
/* 0 to 25 are connected to MIOs (26 pins) */
.
bank_min
[
1
]
=
26
,
.
bank_max
[
1
]
=
51
,
/* Bank 1 are connected to MIOs (26 pins) */
.
bank_min
[
3
]
=
52
,
.
bank_max
[
3
]
=
83
,
/* Bank 3 is connected to EMIOs (32 pins) */
.
bank_min
[
4
]
=
84
,
.
bank_max
[
4
]
=
115
,
/* Bank 4 is connected to EMIOs (32 pins) */
};
static
const
struct
zynq_platform_data
zynqmp_gpio_def
=
{
.
label
=
"zynqmp_gpio"
,
.
quirks
=
GPIO_QUIRK_DATA_RO_BUG
,
...
...
@@ -815,6 +867,8 @@ static const struct zynq_platform_data zynq_gpio_def = {
static
const
struct
of_device_id
zynq_gpio_of_match
[]
=
{
{
.
compatible
=
"xlnx,zynq-gpio-1.0"
,
.
data
=
&
zynq_gpio_def
},
{
.
compatible
=
"xlnx,zynqmp-gpio-1.0"
,
.
data
=
&
zynqmp_gpio_def
},
{
.
compatible
=
"xlnx,versal-gpio-1.0"
,
.
data
=
&
versal_gpio_def
},
{
.
compatible
=
"xlnx,pmc-gpio-1.0"
,
.
data
=
&
pmc_gpio_def
},
{
/* end of table */
}
};
MODULE_DEVICE_TABLE
(
of
,
zynq_gpio_of_match
);
...
...
@@ -876,7 +930,8 @@ static int zynq_gpio_probe(struct platform_device *pdev)
/* Retrieve GPIO clock */
gpio
->
clk
=
devm_clk_get
(
&
pdev
->
dev
,
NULL
);
if
(
IS_ERR
(
gpio
->
clk
))
{
dev_err
(
&
pdev
->
dev
,
"input clock not found.
\n
"
);
if
(
PTR_ERR
(
gpio
->
clk
)
!=
-
EPROBE_DEFER
)
dev_err
(
&
pdev
->
dev
,
"input clock not found.
\n
"
);
return
PTR_ERR
(
gpio
->
clk
);
}
ret
=
clk_prepare_enable
(
gpio
->
clk
);
...
...
@@ -885,6 +940,8 @@ static int zynq_gpio_probe(struct platform_device *pdev)
return
ret
;
}
spin_lock_init
(
&
gpio
->
dirlock
);
pm_runtime_set_active
(
&
pdev
->
dev
);
pm_runtime_enable
(
&
pdev
->
dev
);
ret
=
pm_runtime_get_sync
(
&
pdev
->
dev
);
...
...
@@ -892,9 +949,12 @@ static int zynq_gpio_probe(struct platform_device *pdev)
goto
err_pm_dis
;
/* disable interrupts for all banks */
for
(
bank_num
=
0
;
bank_num
<
gpio
->
p_data
->
max_bank
;
bank_num
++
)
for
(
bank_num
=
0
;
bank_num
<
gpio
->
p_data
->
max_bank
;
bank_num
++
)
{
writel_relaxed
(
ZYNQ_GPIO_IXR_DISABLE_ALL
,
gpio
->
base_addr
+
ZYNQ_GPIO_INTDIS_OFFSET
(
bank_num
));
if
(
gpio
->
p_data
->
quirks
&
GPIO_QUIRK_VERSAL
)
bank_num
=
bank_num
+
VERSAL_UNUSED_BANKS
;
}
/* Set up the GPIO irqchip */
girq
=
&
chip
->
irq
;
...
...
@@ -919,6 +979,8 @@ static int zynq_gpio_probe(struct platform_device *pdev)
goto
err_pm_put
;
}
irq_set_status_flags
(
gpio
->
irq
,
IRQ_DISABLE_UNLAZY
);
device_init_wakeup
(
&
pdev
->
dev
,
1
);
pm_runtime_put
(
&
pdev
->
dev
);
return
0
;
...
...
drivers/gpio/gpiolib-cdev.c
0 → 100644
View file @
4672a4a9
// SPDX-License-Identifier: GPL-2.0
#include <linux/bitmap.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/irqreturn.h>
#include <linux/spinlock.h>
#include <linux/device.h>
#include <linux/err.h>
#include <linux/gpio.h>
#include <linux/gpio/driver.h>
#include <linux/pinctrl/consumer.h>
#include <linux/cdev.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/anon_inodes.h>
#include <linux/file.h>
#include <linux/kfifo.h>
#include <linux/poll.h>
#include <linux/timekeeping.h>
#include <uapi/linux/gpio.h>
#include "gpiolib.h"
#include "gpiolib-cdev.h"
/* Character device interface to GPIO.
*
* The GPIO character device, /dev/gpiochipN, provides userspace an
* interface to gpiolib GPIOs via ioctl()s.
*/
/*
* GPIO line handle management
*/
/**
* struct linehandle_state - contains the state of a userspace handle
* @gdev: the GPIO device the handle pertains to
* @label: consumer label used to tag descriptors
* @descs: the GPIO descriptors held by this handle
* @numdescs: the number of descriptors held in the descs array
*/
struct
linehandle_state
{
struct
gpio_device
*
gdev
;
const
char
*
label
;
struct
gpio_desc
*
descs
[
GPIOHANDLES_MAX
];
u32
numdescs
;
};
#define GPIOHANDLE_REQUEST_VALID_FLAGS \
(GPIOHANDLE_REQUEST_INPUT | \
GPIOHANDLE_REQUEST_OUTPUT | \
GPIOHANDLE_REQUEST_ACTIVE_LOW | \
GPIOHANDLE_REQUEST_BIAS_PULL_UP | \
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \
GPIOHANDLE_REQUEST_BIAS_DISABLE | \
GPIOHANDLE_REQUEST_OPEN_DRAIN | \
GPIOHANDLE_REQUEST_OPEN_SOURCE)
static
int
linehandle_validate_flags
(
u32
flags
)
{
/* Return an error if an unknown flag is set */
if
(
flags
&
~
GPIOHANDLE_REQUEST_VALID_FLAGS
)
return
-
EINVAL
;
/*
* Do not allow both INPUT & OUTPUT flags to be set as they are
* contradictory.
*/
if
((
flags
&
GPIOHANDLE_REQUEST_INPUT
)
&&
(
flags
&
GPIOHANDLE_REQUEST_OUTPUT
))
return
-
EINVAL
;
/*
* Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If
* the hardware actually supports enabling both at the same time the
* electrical result would be disastrous.
*/
if
((
flags
&
GPIOHANDLE_REQUEST_OPEN_DRAIN
)
&&
(
flags
&
GPIOHANDLE_REQUEST_OPEN_SOURCE
))
return
-
EINVAL
;
/* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */
if
(
!
(
flags
&
GPIOHANDLE_REQUEST_OUTPUT
)
&&
((
flags
&
GPIOHANDLE_REQUEST_OPEN_DRAIN
)
||
(
flags
&
GPIOHANDLE_REQUEST_OPEN_SOURCE
)))
return
-
EINVAL
;
/* Bias flags only allowed for input or output mode. */
if
(
!
((
flags
&
GPIOHANDLE_REQUEST_INPUT
)
||
(
flags
&
GPIOHANDLE_REQUEST_OUTPUT
))
&&
((
flags
&
GPIOHANDLE_REQUEST_BIAS_DISABLE
)
||
(
flags
&
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)
||
(
flags
&
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
)))
return
-
EINVAL
;
/* Only one bias flag can be set. */
if
(((
flags
&
GPIOHANDLE_REQUEST_BIAS_DISABLE
)
&&
(
flags
&
(
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
|
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)))
||
((
flags
&
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
)
&&
(
flags
&
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)))
return
-
EINVAL
;
return
0
;
}
static
long
linehandle_set_config
(
struct
linehandle_state
*
lh
,
void
__user
*
ip
)
{
struct
gpiohandle_config
gcnf
;
struct
gpio_desc
*
desc
;
int
i
,
ret
;
u32
lflags
;
unsigned
long
*
flagsp
;
if
(
copy_from_user
(
&
gcnf
,
ip
,
sizeof
(
gcnf
)))
return
-
EFAULT
;
lflags
=
gcnf
.
flags
;
ret
=
linehandle_validate_flags
(
lflags
);
if
(
ret
)
return
ret
;
for
(
i
=
0
;
i
<
lh
->
numdescs
;
i
++
)
{
desc
=
lh
->
descs
[
i
];
flagsp
=
&
desc
->
flags
;
assign_bit
(
FLAG_ACTIVE_LOW
,
flagsp
,
lflags
&
GPIOHANDLE_REQUEST_ACTIVE_LOW
);
assign_bit
(
FLAG_OPEN_DRAIN
,
flagsp
,
lflags
&
GPIOHANDLE_REQUEST_OPEN_DRAIN
);
assign_bit
(
FLAG_OPEN_SOURCE
,
flagsp
,
lflags
&
GPIOHANDLE_REQUEST_OPEN_SOURCE
);
assign_bit
(
FLAG_PULL_UP
,
flagsp
,
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_UP
);
assign_bit
(
FLAG_PULL_DOWN
,
flagsp
,
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
);
assign_bit
(
FLAG_BIAS_DISABLE
,
flagsp
,
lflags
&
GPIOHANDLE_REQUEST_BIAS_DISABLE
);
/*
* Lines have to be requested explicitly for input
* or output, else the line will be treated "as is".
*/
if
(
lflags
&
GPIOHANDLE_REQUEST_OUTPUT
)
{
int
val
=
!!
gcnf
.
default_values
[
i
];
ret
=
gpiod_direction_output
(
desc
,
val
);
if
(
ret
)
return
ret
;
}
else
if
(
lflags
&
GPIOHANDLE_REQUEST_INPUT
)
{
ret
=
gpiod_direction_input
(
desc
);
if
(
ret
)
return
ret
;
}
atomic_notifier_call_chain
(
&
desc
->
gdev
->
notifier
,
GPIOLINE_CHANGED_CONFIG
,
desc
);
}
return
0
;
}
static
long
linehandle_ioctl
(
struct
file
*
filep
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
linehandle_state
*
lh
=
filep
->
private_data
;
void
__user
*
ip
=
(
void
__user
*
)
arg
;
struct
gpiohandle_data
ghd
;
DECLARE_BITMAP
(
vals
,
GPIOHANDLES_MAX
);
int
i
;
if
(
cmd
==
GPIOHANDLE_GET_LINE_VALUES_IOCTL
)
{
/* NOTE: It's ok to read values of output lines. */
int
ret
=
gpiod_get_array_value_complex
(
false
,
true
,
lh
->
numdescs
,
lh
->
descs
,
NULL
,
vals
);
if
(
ret
)
return
ret
;
memset
(
&
ghd
,
0
,
sizeof
(
ghd
));
for
(
i
=
0
;
i
<
lh
->
numdescs
;
i
++
)
ghd
.
values
[
i
]
=
test_bit
(
i
,
vals
);
if
(
copy_to_user
(
ip
,
&
ghd
,
sizeof
(
ghd
)))
return
-
EFAULT
;
return
0
;
}
else
if
(
cmd
==
GPIOHANDLE_SET_LINE_VALUES_IOCTL
)
{
/*
* All line descriptors were created at once with the same
* flags so just check if the first one is really output.
*/
if
(
!
test_bit
(
FLAG_IS_OUT
,
&
lh
->
descs
[
0
]
->
flags
))
return
-
EPERM
;
if
(
copy_from_user
(
&
ghd
,
ip
,
sizeof
(
ghd
)))
return
-
EFAULT
;
/* Clamp all values to [0,1] */
for
(
i
=
0
;
i
<
lh
->
numdescs
;
i
++
)
__assign_bit
(
i
,
vals
,
ghd
.
values
[
i
]);
/* Reuse the array setting function */
return
gpiod_set_array_value_complex
(
false
,
true
,
lh
->
numdescs
,
lh
->
descs
,
NULL
,
vals
);
}
else
if
(
cmd
==
GPIOHANDLE_SET_CONFIG_IOCTL
)
{
return
linehandle_set_config
(
lh
,
ip
);
}
return
-
EINVAL
;
}
#ifdef CONFIG_COMPAT
static
long
linehandle_ioctl_compat
(
struct
file
*
filep
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
return
linehandle_ioctl
(
filep
,
cmd
,
(
unsigned
long
)
compat_ptr
(
arg
));
}
#endif
static
int
linehandle_release
(
struct
inode
*
inode
,
struct
file
*
filep
)
{
struct
linehandle_state
*
lh
=
filep
->
private_data
;
struct
gpio_device
*
gdev
=
lh
->
gdev
;
int
i
;
for
(
i
=
0
;
i
<
lh
->
numdescs
;
i
++
)
gpiod_free
(
lh
->
descs
[
i
]);
kfree
(
lh
->
label
);
kfree
(
lh
);
put_device
(
&
gdev
->
dev
);
return
0
;
}
static
const
struct
file_operations
linehandle_fileops
=
{
.
release
=
linehandle_release
,
.
owner
=
THIS_MODULE
,
.
llseek
=
noop_llseek
,
.
unlocked_ioctl
=
linehandle_ioctl
,
#ifdef CONFIG_COMPAT
.
compat_ioctl
=
linehandle_ioctl_compat
,
#endif
};
static
int
linehandle_create
(
struct
gpio_device
*
gdev
,
void
__user
*
ip
)
{
struct
gpiohandle_request
handlereq
;
struct
linehandle_state
*
lh
;
struct
file
*
file
;
int
fd
,
i
,
count
=
0
,
ret
;
u32
lflags
;
if
(
copy_from_user
(
&
handlereq
,
ip
,
sizeof
(
handlereq
)))
return
-
EFAULT
;
if
((
handlereq
.
lines
==
0
)
||
(
handlereq
.
lines
>
GPIOHANDLES_MAX
))
return
-
EINVAL
;
lflags
=
handlereq
.
flags
;
ret
=
linehandle_validate_flags
(
lflags
);
if
(
ret
)
return
ret
;
lh
=
kzalloc
(
sizeof
(
*
lh
),
GFP_KERNEL
);
if
(
!
lh
)
return
-
ENOMEM
;
lh
->
gdev
=
gdev
;
get_device
(
&
gdev
->
dev
);
/* Make sure this is terminated */
handlereq
.
consumer_label
[
sizeof
(
handlereq
.
consumer_label
)
-
1
]
=
'\0'
;
if
(
strlen
(
handlereq
.
consumer_label
))
{
lh
->
label
=
kstrdup
(
handlereq
.
consumer_label
,
GFP_KERNEL
);
if
(
!
lh
->
label
)
{
ret
=
-
ENOMEM
;
goto
out_free_lh
;
}
}
/* Request each GPIO */
for
(
i
=
0
;
i
<
handlereq
.
lines
;
i
++
)
{
u32
offset
=
handlereq
.
lineoffsets
[
i
];
struct
gpio_desc
*
desc
=
gpiochip_get_desc
(
gdev
->
chip
,
offset
);
if
(
IS_ERR
(
desc
))
{
ret
=
PTR_ERR
(
desc
);
goto
out_free_descs
;
}
ret
=
gpiod_request
(
desc
,
lh
->
label
);
if
(
ret
)
goto
out_free_descs
;
lh
->
descs
[
i
]
=
desc
;
count
=
i
+
1
;
if
(
lflags
&
GPIOHANDLE_REQUEST_ACTIVE_LOW
)
set_bit
(
FLAG_ACTIVE_LOW
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_OPEN_DRAIN
)
set_bit
(
FLAG_OPEN_DRAIN
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_OPEN_SOURCE
)
set_bit
(
FLAG_OPEN_SOURCE
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_DISABLE
)
set_bit
(
FLAG_BIAS_DISABLE
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
)
set_bit
(
FLAG_PULL_DOWN
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)
set_bit
(
FLAG_PULL_UP
,
&
desc
->
flags
);
ret
=
gpiod_set_transitory
(
desc
,
false
);
if
(
ret
<
0
)
goto
out_free_descs
;
/*
* Lines have to be requested explicitly for input
* or output, else the line will be treated "as is".
*/
if
(
lflags
&
GPIOHANDLE_REQUEST_OUTPUT
)
{
int
val
=
!!
handlereq
.
default_values
[
i
];
ret
=
gpiod_direction_output
(
desc
,
val
);
if
(
ret
)
goto
out_free_descs
;
}
else
if
(
lflags
&
GPIOHANDLE_REQUEST_INPUT
)
{
ret
=
gpiod_direction_input
(
desc
);
if
(
ret
)
goto
out_free_descs
;
}
atomic_notifier_call_chain
(
&
desc
->
gdev
->
notifier
,
GPIOLINE_CHANGED_REQUESTED
,
desc
);
dev_dbg
(
&
gdev
->
dev
,
"registered chardev handle for line %d
\n
"
,
offset
);
}
/* Let i point at the last handle */
i
--
;
lh
->
numdescs
=
handlereq
.
lines
;
fd
=
get_unused_fd_flags
(
O_RDONLY
|
O_CLOEXEC
);
if
(
fd
<
0
)
{
ret
=
fd
;
goto
out_free_descs
;
}
file
=
anon_inode_getfile
(
"gpio-linehandle"
,
&
linehandle_fileops
,
lh
,
O_RDONLY
|
O_CLOEXEC
);
if
(
IS_ERR
(
file
))
{
ret
=
PTR_ERR
(
file
);
goto
out_put_unused_fd
;
}
handlereq
.
fd
=
fd
;
if
(
copy_to_user
(
ip
,
&
handlereq
,
sizeof
(
handlereq
)))
{
/*
* fput() will trigger the release() callback, so do not go onto
* the regular error cleanup path here.
*/
fput
(
file
);
put_unused_fd
(
fd
);
return
-
EFAULT
;
}
fd_install
(
fd
,
file
);
dev_dbg
(
&
gdev
->
dev
,
"registered chardev handle for %d lines
\n
"
,
lh
->
numdescs
);
return
0
;
out_put_unused_fd:
put_unused_fd
(
fd
);
out_free_descs:
for
(
i
=
0
;
i
<
count
;
i
++
)
gpiod_free
(
lh
->
descs
[
i
]);
kfree
(
lh
->
label
);
out_free_lh:
kfree
(
lh
);
put_device
(
&
gdev
->
dev
);
return
ret
;
}
/*
* GPIO line event management
*/
/**
* struct lineevent_state - contains the state of a userspace event
* @gdev: the GPIO device the event pertains to
* @label: consumer label used to tag descriptors
* @desc: the GPIO descriptor held by this event
* @eflags: the event flags this line was requested with
* @irq: the interrupt that trigger in response to events on this GPIO
* @wait: wait queue that handles blocking reads of events
* @events: KFIFO for the GPIO events
* @timestamp: cache for the timestamp storing it between hardirq
* and IRQ thread, used to bring the timestamp close to the actual
* event
*/
struct
lineevent_state
{
struct
gpio_device
*
gdev
;
const
char
*
label
;
struct
gpio_desc
*
desc
;
u32
eflags
;
int
irq
;
wait_queue_head_t
wait
;
DECLARE_KFIFO
(
events
,
struct
gpioevent_data
,
16
);
u64
timestamp
;
};
#define GPIOEVENT_REQUEST_VALID_FLAGS \
(GPIOEVENT_REQUEST_RISING_EDGE | \
GPIOEVENT_REQUEST_FALLING_EDGE)
static
__poll_t
lineevent_poll
(
struct
file
*
filep
,
struct
poll_table_struct
*
wait
)
{
struct
lineevent_state
*
le
=
filep
->
private_data
;
__poll_t
events
=
0
;
poll_wait
(
filep
,
&
le
->
wait
,
wait
);
if
(
!
kfifo_is_empty_spinlocked_noirqsave
(
&
le
->
events
,
&
le
->
wait
.
lock
))
events
=
EPOLLIN
|
EPOLLRDNORM
;
return
events
;
}
static
ssize_t
lineevent_read
(
struct
file
*
filep
,
char
__user
*
buf
,
size_t
count
,
loff_t
*
f_ps
)
{
struct
lineevent_state
*
le
=
filep
->
private_data
;
struct
gpioevent_data
ge
;
ssize_t
bytes_read
=
0
;
int
ret
;
if
(
count
<
sizeof
(
ge
))
return
-
EINVAL
;
do
{
spin_lock
(
&
le
->
wait
.
lock
);
if
(
kfifo_is_empty
(
&
le
->
events
))
{
if
(
bytes_read
)
{
spin_unlock
(
&
le
->
wait
.
lock
);
return
bytes_read
;
}
if
(
filep
->
f_flags
&
O_NONBLOCK
)
{
spin_unlock
(
&
le
->
wait
.
lock
);
return
-
EAGAIN
;
}
ret
=
wait_event_interruptible_locked
(
le
->
wait
,
!
kfifo_is_empty
(
&
le
->
events
));
if
(
ret
)
{
spin_unlock
(
&
le
->
wait
.
lock
);
return
ret
;
}
}
ret
=
kfifo_out
(
&
le
->
events
,
&
ge
,
1
);
spin_unlock
(
&
le
->
wait
.
lock
);
if
(
ret
!=
1
)
{
/*
* This should never happen - we were holding the lock
* from the moment we learned the fifo is no longer
* empty until now.
*/
ret
=
-
EIO
;
break
;
}
if
(
copy_to_user
(
buf
+
bytes_read
,
&
ge
,
sizeof
(
ge
)))
return
-
EFAULT
;
bytes_read
+=
sizeof
(
ge
);
}
while
(
count
>=
bytes_read
+
sizeof
(
ge
));
return
bytes_read
;
}
static
int
lineevent_release
(
struct
inode
*
inode
,
struct
file
*
filep
)
{
struct
lineevent_state
*
le
=
filep
->
private_data
;
struct
gpio_device
*
gdev
=
le
->
gdev
;
free_irq
(
le
->
irq
,
le
);
gpiod_free
(
le
->
desc
);
kfree
(
le
->
label
);
kfree
(
le
);
put_device
(
&
gdev
->
dev
);
return
0
;
}
static
long
lineevent_ioctl
(
struct
file
*
filep
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
lineevent_state
*
le
=
filep
->
private_data
;
void
__user
*
ip
=
(
void
__user
*
)
arg
;
struct
gpiohandle_data
ghd
;
/*
* We can get the value for an event line but not set it,
* because it is input by definition.
*/
if
(
cmd
==
GPIOHANDLE_GET_LINE_VALUES_IOCTL
)
{
int
val
;
memset
(
&
ghd
,
0
,
sizeof
(
ghd
));
val
=
gpiod_get_value_cansleep
(
le
->
desc
);
if
(
val
<
0
)
return
val
;
ghd
.
values
[
0
]
=
val
;
if
(
copy_to_user
(
ip
,
&
ghd
,
sizeof
(
ghd
)))
return
-
EFAULT
;
return
0
;
}
return
-
EINVAL
;
}
#ifdef CONFIG_COMPAT
static
long
lineevent_ioctl_compat
(
struct
file
*
filep
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
return
lineevent_ioctl
(
filep
,
cmd
,
(
unsigned
long
)
compat_ptr
(
arg
));
}
#endif
static
const
struct
file_operations
lineevent_fileops
=
{
.
release
=
lineevent_release
,
.
read
=
lineevent_read
,
.
poll
=
lineevent_poll
,
.
owner
=
THIS_MODULE
,
.
llseek
=
noop_llseek
,
.
unlocked_ioctl
=
lineevent_ioctl
,
#ifdef CONFIG_COMPAT
.
compat_ioctl
=
lineevent_ioctl_compat
,
#endif
};
static
irqreturn_t
lineevent_irq_thread
(
int
irq
,
void
*
p
)
{
struct
lineevent_state
*
le
=
p
;
struct
gpioevent_data
ge
;
int
ret
;
/* Do not leak kernel stack to userspace */
memset
(
&
ge
,
0
,
sizeof
(
ge
));
/*
* We may be running from a nested threaded interrupt in which case
* we didn't get the timestamp from lineevent_irq_handler().
*/
if
(
!
le
->
timestamp
)
ge
.
timestamp
=
ktime_get_ns
();
else
ge
.
timestamp
=
le
->
timestamp
;
if
(
le
->
eflags
&
GPIOEVENT_REQUEST_RISING_EDGE
&&
le
->
eflags
&
GPIOEVENT_REQUEST_FALLING_EDGE
)
{
int
level
=
gpiod_get_value_cansleep
(
le
->
desc
);
if
(
level
)
/* Emit low-to-high event */
ge
.
id
=
GPIOEVENT_EVENT_RISING_EDGE
;
else
/* Emit high-to-low event */
ge
.
id
=
GPIOEVENT_EVENT_FALLING_EDGE
;
}
else
if
(
le
->
eflags
&
GPIOEVENT_REQUEST_RISING_EDGE
)
{
/* Emit low-to-high event */
ge
.
id
=
GPIOEVENT_EVENT_RISING_EDGE
;
}
else
if
(
le
->
eflags
&
GPIOEVENT_REQUEST_FALLING_EDGE
)
{
/* Emit high-to-low event */
ge
.
id
=
GPIOEVENT_EVENT_FALLING_EDGE
;
}
else
{
return
IRQ_NONE
;
}
ret
=
kfifo_in_spinlocked_noirqsave
(
&
le
->
events
,
&
ge
,
1
,
&
le
->
wait
.
lock
);
if
(
ret
)
wake_up_poll
(
&
le
->
wait
,
EPOLLIN
);
else
pr_debug_ratelimited
(
"event FIFO is full - event dropped
\n
"
);
return
IRQ_HANDLED
;
}
static
irqreturn_t
lineevent_irq_handler
(
int
irq
,
void
*
p
)
{
struct
lineevent_state
*
le
=
p
;
/*
* Just store the timestamp in hardirq context so we get it as
* close in time as possible to the actual event.
*/
le
->
timestamp
=
ktime_get_ns
();
return
IRQ_WAKE_THREAD
;
}
static
int
lineevent_create
(
struct
gpio_device
*
gdev
,
void
__user
*
ip
)
{
struct
gpioevent_request
eventreq
;
struct
lineevent_state
*
le
;
struct
gpio_desc
*
desc
;
struct
file
*
file
;
u32
offset
;
u32
lflags
;
u32
eflags
;
int
fd
;
int
ret
;
int
irqflags
=
0
;
if
(
copy_from_user
(
&
eventreq
,
ip
,
sizeof
(
eventreq
)))
return
-
EFAULT
;
offset
=
eventreq
.
lineoffset
;
lflags
=
eventreq
.
handleflags
;
eflags
=
eventreq
.
eventflags
;
desc
=
gpiochip_get_desc
(
gdev
->
chip
,
offset
);
if
(
IS_ERR
(
desc
))
return
PTR_ERR
(
desc
);
/* Return an error if a unknown flag is set */
if
((
lflags
&
~
GPIOHANDLE_REQUEST_VALID_FLAGS
)
||
(
eflags
&
~
GPIOEVENT_REQUEST_VALID_FLAGS
))
return
-
EINVAL
;
/* This is just wrong: we don't look for events on output lines */
if
((
lflags
&
GPIOHANDLE_REQUEST_OUTPUT
)
||
(
lflags
&
GPIOHANDLE_REQUEST_OPEN_DRAIN
)
||
(
lflags
&
GPIOHANDLE_REQUEST_OPEN_SOURCE
))
return
-
EINVAL
;
/* Only one bias flag can be set. */
if
(((
lflags
&
GPIOHANDLE_REQUEST_BIAS_DISABLE
)
&&
(
lflags
&
(
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
|
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)))
||
((
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
)
&&
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)))
return
-
EINVAL
;
le
=
kzalloc
(
sizeof
(
*
le
),
GFP_KERNEL
);
if
(
!
le
)
return
-
ENOMEM
;
le
->
gdev
=
gdev
;
get_device
(
&
gdev
->
dev
);
/* Make sure this is terminated */
eventreq
.
consumer_label
[
sizeof
(
eventreq
.
consumer_label
)
-
1
]
=
'\0'
;
if
(
strlen
(
eventreq
.
consumer_label
))
{
le
->
label
=
kstrdup
(
eventreq
.
consumer_label
,
GFP_KERNEL
);
if
(
!
le
->
label
)
{
ret
=
-
ENOMEM
;
goto
out_free_le
;
}
}
ret
=
gpiod_request
(
desc
,
le
->
label
);
if
(
ret
)
goto
out_free_label
;
le
->
desc
=
desc
;
le
->
eflags
=
eflags
;
if
(
lflags
&
GPIOHANDLE_REQUEST_ACTIVE_LOW
)
set_bit
(
FLAG_ACTIVE_LOW
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_DISABLE
)
set_bit
(
FLAG_BIAS_DISABLE
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
)
set_bit
(
FLAG_PULL_DOWN
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)
set_bit
(
FLAG_PULL_UP
,
&
desc
->
flags
);
ret
=
gpiod_direction_input
(
desc
);
if
(
ret
)
goto
out_free_desc
;
atomic_notifier_call_chain
(
&
desc
->
gdev
->
notifier
,
GPIOLINE_CHANGED_REQUESTED
,
desc
);
le
->
irq
=
gpiod_to_irq
(
desc
);
if
(
le
->
irq
<=
0
)
{
ret
=
-
ENODEV
;
goto
out_free_desc
;
}
if
(
eflags
&
GPIOEVENT_REQUEST_RISING_EDGE
)
irqflags
|=
test_bit
(
FLAG_ACTIVE_LOW
,
&
desc
->
flags
)
?
IRQF_TRIGGER_FALLING
:
IRQF_TRIGGER_RISING
;
if
(
eflags
&
GPIOEVENT_REQUEST_FALLING_EDGE
)
irqflags
|=
test_bit
(
FLAG_ACTIVE_LOW
,
&
desc
->
flags
)
?
IRQF_TRIGGER_RISING
:
IRQF_TRIGGER_FALLING
;
irqflags
|=
IRQF_ONESHOT
;
INIT_KFIFO
(
le
->
events
);
init_waitqueue_head
(
&
le
->
wait
);
/* Request a thread to read the events */
ret
=
request_threaded_irq
(
le
->
irq
,
lineevent_irq_handler
,
lineevent_irq_thread
,
irqflags
,
le
->
label
,
le
);
if
(
ret
)
goto
out_free_desc
;
fd
=
get_unused_fd_flags
(
O_RDONLY
|
O_CLOEXEC
);
if
(
fd
<
0
)
{
ret
=
fd
;
goto
out_free_irq
;
}
file
=
anon_inode_getfile
(
"gpio-event"
,
&
lineevent_fileops
,
le
,
O_RDONLY
|
O_CLOEXEC
);
if
(
IS_ERR
(
file
))
{
ret
=
PTR_ERR
(
file
);
goto
out_put_unused_fd
;
}
eventreq
.
fd
=
fd
;
if
(
copy_to_user
(
ip
,
&
eventreq
,
sizeof
(
eventreq
)))
{
/*
* fput() will trigger the release() callback, so do not go onto
* the regular error cleanup path here.
*/
fput
(
file
);
put_unused_fd
(
fd
);
return
-
EFAULT
;
}
fd_install
(
fd
,
file
);
return
0
;
out_put_unused_fd:
put_unused_fd
(
fd
);
out_free_irq:
free_irq
(
le
->
irq
,
le
);
out_free_desc:
gpiod_free
(
le
->
desc
);
out_free_label:
kfree
(
le
->
label
);
out_free_le:
kfree
(
le
);
put_device
(
&
gdev
->
dev
);
return
ret
;
}
static
void
gpio_desc_to_lineinfo
(
struct
gpio_desc
*
desc
,
struct
gpioline_info
*
info
)
{
struct
gpio_chip
*
gc
=
desc
->
gdev
->
chip
;
bool
ok_for_pinctrl
;
unsigned
long
flags
;
/*
* This function takes a mutex so we must check this before taking
* the spinlock.
*
* FIXME: find a non-racy way to retrieve this information. Maybe a
* lock common to both frameworks?
*/
ok_for_pinctrl
=
pinctrl_gpio_can_use_line
(
gc
->
base
+
info
->
line_offset
);
spin_lock_irqsave
(
&
gpio_lock
,
flags
);
if
(
desc
->
name
)
{
strncpy
(
info
->
name
,
desc
->
name
,
sizeof
(
info
->
name
));
info
->
name
[
sizeof
(
info
->
name
)
-
1
]
=
'\0'
;
}
else
{
info
->
name
[
0
]
=
'\0'
;
}
if
(
desc
->
label
)
{
strncpy
(
info
->
consumer
,
desc
->
label
,
sizeof
(
info
->
consumer
));
info
->
consumer
[
sizeof
(
info
->
consumer
)
-
1
]
=
'\0'
;
}
else
{
info
->
consumer
[
0
]
=
'\0'
;
}
/*
* Userspace only need to know that the kernel is using this GPIO so
* it can't use it.
*/
info
->
flags
=
0
;
if
(
test_bit
(
FLAG_REQUESTED
,
&
desc
->
flags
)
||
test_bit
(
FLAG_IS_HOGGED
,
&
desc
->
flags
)
||
test_bit
(
FLAG_USED_AS_IRQ
,
&
desc
->
flags
)
||
test_bit
(
FLAG_EXPORT
,
&
desc
->
flags
)
||
test_bit
(
FLAG_SYSFS
,
&
desc
->
flags
)
||
!
ok_for_pinctrl
)
info
->
flags
|=
GPIOLINE_FLAG_KERNEL
;
if
(
test_bit
(
FLAG_IS_OUT
,
&
desc
->
flags
))
info
->
flags
|=
GPIOLINE_FLAG_IS_OUT
;
if
(
test_bit
(
FLAG_ACTIVE_LOW
,
&
desc
->
flags
))
info
->
flags
|=
GPIOLINE_FLAG_ACTIVE_LOW
;
if
(
test_bit
(
FLAG_OPEN_DRAIN
,
&
desc
->
flags
))
info
->
flags
|=
(
GPIOLINE_FLAG_OPEN_DRAIN
|
GPIOLINE_FLAG_IS_OUT
);
if
(
test_bit
(
FLAG_OPEN_SOURCE
,
&
desc
->
flags
))
info
->
flags
|=
(
GPIOLINE_FLAG_OPEN_SOURCE
|
GPIOLINE_FLAG_IS_OUT
);
if
(
test_bit
(
FLAG_BIAS_DISABLE
,
&
desc
->
flags
))
info
->
flags
|=
GPIOLINE_FLAG_BIAS_DISABLE
;
if
(
test_bit
(
FLAG_PULL_DOWN
,
&
desc
->
flags
))
info
->
flags
|=
GPIOLINE_FLAG_BIAS_PULL_DOWN
;
if
(
test_bit
(
FLAG_PULL_UP
,
&
desc
->
flags
))
info
->
flags
|=
GPIOLINE_FLAG_BIAS_PULL_UP
;
spin_unlock_irqrestore
(
&
gpio_lock
,
flags
);
}
struct
gpio_chardev_data
{
struct
gpio_device
*
gdev
;
wait_queue_head_t
wait
;
DECLARE_KFIFO
(
events
,
struct
gpioline_info_changed
,
32
);
struct
notifier_block
lineinfo_changed_nb
;
unsigned
long
*
watched_lines
;
};
/*
* gpio_ioctl() - ioctl handler for the GPIO chardev
*/
static
long
gpio_ioctl
(
struct
file
*
filp
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
gpio_chardev_data
*
priv
=
filp
->
private_data
;
struct
gpio_device
*
gdev
=
priv
->
gdev
;
struct
gpio_chip
*
gc
=
gdev
->
chip
;
void
__user
*
ip
=
(
void
__user
*
)
arg
;
struct
gpio_desc
*
desc
;
__u32
offset
;
int
hwgpio
;
/* We fail any subsequent ioctl():s when the chip is gone */
if
(
!
gc
)
return
-
ENODEV
;
/* Fill in the struct and pass to userspace */
if
(
cmd
==
GPIO_GET_CHIPINFO_IOCTL
)
{
struct
gpiochip_info
chipinfo
;
memset
(
&
chipinfo
,
0
,
sizeof
(
chipinfo
));
strncpy
(
chipinfo
.
name
,
dev_name
(
&
gdev
->
dev
),
sizeof
(
chipinfo
.
name
));
chipinfo
.
name
[
sizeof
(
chipinfo
.
name
)
-
1
]
=
'\0'
;
strncpy
(
chipinfo
.
label
,
gdev
->
label
,
sizeof
(
chipinfo
.
label
));
chipinfo
.
label
[
sizeof
(
chipinfo
.
label
)
-
1
]
=
'\0'
;
chipinfo
.
lines
=
gdev
->
ngpio
;
if
(
copy_to_user
(
ip
,
&
chipinfo
,
sizeof
(
chipinfo
)))
return
-
EFAULT
;
return
0
;
}
else
if
(
cmd
==
GPIO_GET_LINEINFO_IOCTL
)
{
struct
gpioline_info
lineinfo
;
if
(
copy_from_user
(
&
lineinfo
,
ip
,
sizeof
(
lineinfo
)))
return
-
EFAULT
;
desc
=
gpiochip_get_desc
(
gc
,
lineinfo
.
line_offset
);
if
(
IS_ERR
(
desc
))
return
PTR_ERR
(
desc
);
hwgpio
=
gpio_chip_hwgpio
(
desc
);
gpio_desc_to_lineinfo
(
desc
,
&
lineinfo
);
if
(
copy_to_user
(
ip
,
&
lineinfo
,
sizeof
(
lineinfo
)))
return
-
EFAULT
;
return
0
;
}
else
if
(
cmd
==
GPIO_GET_LINEHANDLE_IOCTL
)
{
return
linehandle_create
(
gdev
,
ip
);
}
else
if
(
cmd
==
GPIO_GET_LINEEVENT_IOCTL
)
{
return
lineevent_create
(
gdev
,
ip
);
}
else
if
(
cmd
==
GPIO_GET_LINEINFO_WATCH_IOCTL
)
{
struct
gpioline_info
lineinfo
;
if
(
copy_from_user
(
&
lineinfo
,
ip
,
sizeof
(
lineinfo
)))
return
-
EFAULT
;
desc
=
gpiochip_get_desc
(
gc
,
lineinfo
.
line_offset
);
if
(
IS_ERR
(
desc
))
return
PTR_ERR
(
desc
);
hwgpio
=
gpio_chip_hwgpio
(
desc
);
if
(
test_bit
(
hwgpio
,
priv
->
watched_lines
))
return
-
EBUSY
;
gpio_desc_to_lineinfo
(
desc
,
&
lineinfo
);
if
(
copy_to_user
(
ip
,
&
lineinfo
,
sizeof
(
lineinfo
)))
return
-
EFAULT
;
set_bit
(
hwgpio
,
priv
->
watched_lines
);
return
0
;
}
else
if
(
cmd
==
GPIO_GET_LINEINFO_UNWATCH_IOCTL
)
{
if
(
copy_from_user
(
&
offset
,
ip
,
sizeof
(
offset
)))
return
-
EFAULT
;
desc
=
gpiochip_get_desc
(
gc
,
offset
);
if
(
IS_ERR
(
desc
))
return
PTR_ERR
(
desc
);
hwgpio
=
gpio_chip_hwgpio
(
desc
);
if
(
!
test_bit
(
hwgpio
,
priv
->
watched_lines
))
return
-
EBUSY
;
clear_bit
(
hwgpio
,
priv
->
watched_lines
);
return
0
;
}
return
-
EINVAL
;
}
#ifdef CONFIG_COMPAT
static
long
gpio_ioctl_compat
(
struct
file
*
filp
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
return
gpio_ioctl
(
filp
,
cmd
,
(
unsigned
long
)
compat_ptr
(
arg
));
}
#endif
static
struct
gpio_chardev_data
*
to_gpio_chardev_data
(
struct
notifier_block
*
nb
)
{
return
container_of
(
nb
,
struct
gpio_chardev_data
,
lineinfo_changed_nb
);
}
static
int
lineinfo_changed_notify
(
struct
notifier_block
*
nb
,
unsigned
long
action
,
void
*
data
)
{
struct
gpio_chardev_data
*
priv
=
to_gpio_chardev_data
(
nb
);
struct
gpioline_info_changed
chg
;
struct
gpio_desc
*
desc
=
data
;
int
ret
;
if
(
!
test_bit
(
gpio_chip_hwgpio
(
desc
),
priv
->
watched_lines
))
return
NOTIFY_DONE
;
memset
(
&
chg
,
0
,
sizeof
(
chg
));
chg
.
info
.
line_offset
=
gpio_chip_hwgpio
(
desc
);
chg
.
event_type
=
action
;
chg
.
timestamp
=
ktime_get_ns
();
gpio_desc_to_lineinfo
(
desc
,
&
chg
.
info
);
ret
=
kfifo_in_spinlocked
(
&
priv
->
events
,
&
chg
,
1
,
&
priv
->
wait
.
lock
);
if
(
ret
)
wake_up_poll
(
&
priv
->
wait
,
EPOLLIN
);
else
pr_debug_ratelimited
(
"lineinfo event FIFO is full - event dropped
\n
"
);
return
NOTIFY_OK
;
}
static
__poll_t
lineinfo_watch_poll
(
struct
file
*
filep
,
struct
poll_table_struct
*
pollt
)
{
struct
gpio_chardev_data
*
priv
=
filep
->
private_data
;
__poll_t
events
=
0
;
poll_wait
(
filep
,
&
priv
->
wait
,
pollt
);
if
(
!
kfifo_is_empty_spinlocked_noirqsave
(
&
priv
->
events
,
&
priv
->
wait
.
lock
))
events
=
EPOLLIN
|
EPOLLRDNORM
;
return
events
;
}
static
ssize_t
lineinfo_watch_read
(
struct
file
*
filep
,
char
__user
*
buf
,
size_t
count
,
loff_t
*
off
)
{
struct
gpio_chardev_data
*
priv
=
filep
->
private_data
;
struct
gpioline_info_changed
event
;
ssize_t
bytes_read
=
0
;
int
ret
;
if
(
count
<
sizeof
(
event
))
return
-
EINVAL
;
do
{
spin_lock
(
&
priv
->
wait
.
lock
);
if
(
kfifo_is_empty
(
&
priv
->
events
))
{
if
(
bytes_read
)
{
spin_unlock
(
&
priv
->
wait
.
lock
);
return
bytes_read
;
}
if
(
filep
->
f_flags
&
O_NONBLOCK
)
{
spin_unlock
(
&
priv
->
wait
.
lock
);
return
-
EAGAIN
;
}
ret
=
wait_event_interruptible_locked
(
priv
->
wait
,
!
kfifo_is_empty
(
&
priv
->
events
));
if
(
ret
)
{
spin_unlock
(
&
priv
->
wait
.
lock
);
return
ret
;
}
}
ret
=
kfifo_out
(
&
priv
->
events
,
&
event
,
1
);
spin_unlock
(
&
priv
->
wait
.
lock
);
if
(
ret
!=
1
)
{
ret
=
-
EIO
;
break
;
/* We should never get here. See lineevent_read(). */
}
if
(
copy_to_user
(
buf
+
bytes_read
,
&
event
,
sizeof
(
event
)))
return
-
EFAULT
;
bytes_read
+=
sizeof
(
event
);
}
while
(
count
>=
bytes_read
+
sizeof
(
event
));
return
bytes_read
;
}
/**
* gpio_chrdev_open() - open the chardev for ioctl operations
* @inode: inode for this chardev
* @filp: file struct for storing private data
* Returns 0 on success
*/
static
int
gpio_chrdev_open
(
struct
inode
*
inode
,
struct
file
*
filp
)
{
struct
gpio_device
*
gdev
=
container_of
(
inode
->
i_cdev
,
struct
gpio_device
,
chrdev
);
struct
gpio_chardev_data
*
priv
;
int
ret
=
-
ENOMEM
;
/* Fail on open if the backing gpiochip is gone */
if
(
!
gdev
->
chip
)
return
-
ENODEV
;
priv
=
kzalloc
(
sizeof
(
*
priv
),
GFP_KERNEL
);
if
(
!
priv
)
return
-
ENOMEM
;
priv
->
watched_lines
=
bitmap_zalloc
(
gdev
->
chip
->
ngpio
,
GFP_KERNEL
);
if
(
!
priv
->
watched_lines
)
goto
out_free_priv
;
init_waitqueue_head
(
&
priv
->
wait
);
INIT_KFIFO
(
priv
->
events
);
priv
->
gdev
=
gdev
;
priv
->
lineinfo_changed_nb
.
notifier_call
=
lineinfo_changed_notify
;
ret
=
atomic_notifier_chain_register
(
&
gdev
->
notifier
,
&
priv
->
lineinfo_changed_nb
);
if
(
ret
)
goto
out_free_bitmap
;
get_device
(
&
gdev
->
dev
);
filp
->
private_data
=
priv
;
ret
=
nonseekable_open
(
inode
,
filp
);
if
(
ret
)
goto
out_unregister_notifier
;
return
ret
;
out_unregister_notifier:
atomic_notifier_chain_unregister
(
&
gdev
->
notifier
,
&
priv
->
lineinfo_changed_nb
);
out_free_bitmap:
bitmap_free
(
priv
->
watched_lines
);
out_free_priv:
kfree
(
priv
);
return
ret
;
}
/**
* gpio_chrdev_release() - close chardev after ioctl operations
* @inode: inode for this chardev
* @filp: file struct for storing private data
* Returns 0 on success
*/
static
int
gpio_chrdev_release
(
struct
inode
*
inode
,
struct
file
*
filp
)
{
struct
gpio_chardev_data
*
priv
=
filp
->
private_data
;
struct
gpio_device
*
gdev
=
priv
->
gdev
;
bitmap_free
(
priv
->
watched_lines
);
atomic_notifier_chain_unregister
(
&
gdev
->
notifier
,
&
priv
->
lineinfo_changed_nb
);
put_device
(
&
gdev
->
dev
);
kfree
(
priv
);
return
0
;
}
static
const
struct
file_operations
gpio_fileops
=
{
.
release
=
gpio_chrdev_release
,
.
open
=
gpio_chrdev_open
,
.
poll
=
lineinfo_watch_poll
,
.
read
=
lineinfo_watch_read
,
.
owner
=
THIS_MODULE
,
.
llseek
=
no_llseek
,
.
unlocked_ioctl
=
gpio_ioctl
,
#ifdef CONFIG_COMPAT
.
compat_ioctl
=
gpio_ioctl_compat
,
#endif
};
int
gpiolib_cdev_register
(
struct
gpio_device
*
gdev
,
dev_t
devt
)
{
int
ret
;
cdev_init
(
&
gdev
->
chrdev
,
&
gpio_fileops
);
gdev
->
chrdev
.
owner
=
THIS_MODULE
;
gdev
->
dev
.
devt
=
MKDEV
(
MAJOR
(
devt
),
gdev
->
id
);
ret
=
cdev_device_add
(
&
gdev
->
chrdev
,
&
gdev
->
dev
);
if
(
ret
)
return
ret
;
chip_dbg
(
gdev
->
chip
,
"added GPIO chardev (%d:%d)
\n
"
,
MAJOR
(
devt
),
gdev
->
id
);
return
0
;
}
void
gpiolib_cdev_unregister
(
struct
gpio_device
*
gdev
)
{
cdev_device_del
(
&
gdev
->
chrdev
,
&
gdev
->
dev
);
}
drivers/gpio/gpiolib-cdev.h
0 → 100644
View file @
4672a4a9
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef GPIOLIB_CDEV_H
#define GPIOLIB_CDEV_H
#include <linux/device.h>
int
gpiolib_cdev_register
(
struct
gpio_device
*
gdev
,
dev_t
devt
);
void
gpiolib_cdev_unregister
(
struct
gpio_device
*
gdev
);
#endif
/* GPIOLIB_CDEV_H */
drivers/gpio/gpiolib-of.c
View file @
4672a4a9
...
...
@@ -25,6 +25,9 @@
/**
* of_gpio_spi_cs_get_count() - special GPIO counting for SPI
* @dev: Consuming device
* @con_id: Function within the GPIO consumer
*
* Some elder GPIO controllers need special quirks. Currently we handle
* the Freescale GPIO controller with bindings that doesn't use the
* established "cs-gpios" for chip selects but instead rely on
...
...
drivers/gpio/gpiolib-sysfs.c
View file @
4672a4a9
...
...
@@ -365,7 +365,7 @@ static DEVICE_ATTR_RW(active_low);
static
umode_t
gpio_is_visible
(
struct
kobject
*
kobj
,
struct
attribute
*
attr
,
int
n
)
{
struct
device
*
dev
=
container_of
(
kobj
,
struct
device
,
kobj
);
struct
device
*
dev
=
kobj_to_dev
(
kobj
);
struct
gpiod_data
*
data
=
dev_get_drvdata
(
dev
);
struct
gpio_desc
*
desc
=
data
->
desc
;
umode_t
mode
=
attr
->
mode
;
...
...
drivers/gpio/gpiolib.c
View file @
4672a4a9
...
...
@@ -17,20 +17,15 @@
#include <linux/gpio/driver.h>
#include <linux/gpio/machine.h>
#include <linux/pinctrl/consumer.h>
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>
#include <linux/compat.h>
#include <linux/anon_inodes.h>
#include <linux/file.h>
#include <linux/kfifo.h>
#include <linux/poll.h>
#include <linux/timekeeping.h>
#include <uapi/linux/gpio.h>
#include "gpiolib.h"
#include "gpiolib-of.h"
#include "gpiolib-acpi.h"
#include "gpiolib-cdev.h"
#define CREATE_TRACE_POINTS
#include <trace/events/gpio.h>
...
...
@@ -425,1105 +420,6 @@ bool gpiochip_line_is_valid(const struct gpio_chip *gc,
}
EXPORT_SYMBOL_GPL
(
gpiochip_line_is_valid
);
/*
* GPIO line handle management
*/
/**
* struct linehandle_state - contains the state of a userspace handle
* @gdev: the GPIO device the handle pertains to
* @label: consumer label used to tag descriptors
* @descs: the GPIO descriptors held by this handle
* @numdescs: the number of descriptors held in the descs array
*/
struct
linehandle_state
{
struct
gpio_device
*
gdev
;
const
char
*
label
;
struct
gpio_desc
*
descs
[
GPIOHANDLES_MAX
];
u32
numdescs
;
};
#define GPIOHANDLE_REQUEST_VALID_FLAGS \
(GPIOHANDLE_REQUEST_INPUT | \
GPIOHANDLE_REQUEST_OUTPUT | \
GPIOHANDLE_REQUEST_ACTIVE_LOW | \
GPIOHANDLE_REQUEST_BIAS_PULL_UP | \
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN | \
GPIOHANDLE_REQUEST_BIAS_DISABLE | \
GPIOHANDLE_REQUEST_OPEN_DRAIN | \
GPIOHANDLE_REQUEST_OPEN_SOURCE)
static
int
linehandle_validate_flags
(
u32
flags
)
{
/* Return an error if an unknown flag is set */
if
(
flags
&
~
GPIOHANDLE_REQUEST_VALID_FLAGS
)
return
-
EINVAL
;
/*
* Do not allow both INPUT & OUTPUT flags to be set as they are
* contradictory.
*/
if
((
flags
&
GPIOHANDLE_REQUEST_INPUT
)
&&
(
flags
&
GPIOHANDLE_REQUEST_OUTPUT
))
return
-
EINVAL
;
/*
* Do not allow OPEN_SOURCE & OPEN_DRAIN flags in a single request. If
* the hardware actually supports enabling both at the same time the
* electrical result would be disastrous.
*/
if
((
flags
&
GPIOHANDLE_REQUEST_OPEN_DRAIN
)
&&
(
flags
&
GPIOHANDLE_REQUEST_OPEN_SOURCE
))
return
-
EINVAL
;
/* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */
if
(
!
(
flags
&
GPIOHANDLE_REQUEST_OUTPUT
)
&&
((
flags
&
GPIOHANDLE_REQUEST_OPEN_DRAIN
)
||
(
flags
&
GPIOHANDLE_REQUEST_OPEN_SOURCE
)))
return
-
EINVAL
;
/* Bias flags only allowed for input or output mode. */
if
(
!
((
flags
&
GPIOHANDLE_REQUEST_INPUT
)
||
(
flags
&
GPIOHANDLE_REQUEST_OUTPUT
))
&&
((
flags
&
GPIOHANDLE_REQUEST_BIAS_DISABLE
)
||
(
flags
&
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)
||
(
flags
&
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
)))
return
-
EINVAL
;
/* Only one bias flag can be set. */
if
(((
flags
&
GPIOHANDLE_REQUEST_BIAS_DISABLE
)
&&
(
flags
&
(
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
|
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)))
||
((
flags
&
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
)
&&
(
flags
&
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)))
return
-
EINVAL
;
return
0
;
}
static
long
linehandle_set_config
(
struct
linehandle_state
*
lh
,
void
__user
*
ip
)
{
struct
gpiohandle_config
gcnf
;
struct
gpio_desc
*
desc
;
int
i
,
ret
;
u32
lflags
;
unsigned
long
*
flagsp
;
if
(
copy_from_user
(
&
gcnf
,
ip
,
sizeof
(
gcnf
)))
return
-
EFAULT
;
lflags
=
gcnf
.
flags
;
ret
=
linehandle_validate_flags
(
lflags
);
if
(
ret
)
return
ret
;
for
(
i
=
0
;
i
<
lh
->
numdescs
;
i
++
)
{
desc
=
lh
->
descs
[
i
];
flagsp
=
&
desc
->
flags
;
assign_bit
(
FLAG_ACTIVE_LOW
,
flagsp
,
lflags
&
GPIOHANDLE_REQUEST_ACTIVE_LOW
);
assign_bit
(
FLAG_OPEN_DRAIN
,
flagsp
,
lflags
&
GPIOHANDLE_REQUEST_OPEN_DRAIN
);
assign_bit
(
FLAG_OPEN_SOURCE
,
flagsp
,
lflags
&
GPIOHANDLE_REQUEST_OPEN_SOURCE
);
assign_bit
(
FLAG_PULL_UP
,
flagsp
,
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_UP
);
assign_bit
(
FLAG_PULL_DOWN
,
flagsp
,
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
);
assign_bit
(
FLAG_BIAS_DISABLE
,
flagsp
,
lflags
&
GPIOHANDLE_REQUEST_BIAS_DISABLE
);
/*
* Lines have to be requested explicitly for input
* or output, else the line will be treated "as is".
*/
if
(
lflags
&
GPIOHANDLE_REQUEST_OUTPUT
)
{
int
val
=
!!
gcnf
.
default_values
[
i
];
ret
=
gpiod_direction_output
(
desc
,
val
);
if
(
ret
)
return
ret
;
}
else
if
(
lflags
&
GPIOHANDLE_REQUEST_INPUT
)
{
ret
=
gpiod_direction_input
(
desc
);
if
(
ret
)
return
ret
;
}
atomic_notifier_call_chain
(
&
desc
->
gdev
->
notifier
,
GPIOLINE_CHANGED_CONFIG
,
desc
);
}
return
0
;
}
static
long
linehandle_ioctl
(
struct
file
*
filep
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
linehandle_state
*
lh
=
filep
->
private_data
;
void
__user
*
ip
=
(
void
__user
*
)
arg
;
struct
gpiohandle_data
ghd
;
DECLARE_BITMAP
(
vals
,
GPIOHANDLES_MAX
);
int
i
;
if
(
cmd
==
GPIOHANDLE_GET_LINE_VALUES_IOCTL
)
{
/* NOTE: It's ok to read values of output lines. */
int
ret
=
gpiod_get_array_value_complex
(
false
,
true
,
lh
->
numdescs
,
lh
->
descs
,
NULL
,
vals
);
if
(
ret
)
return
ret
;
memset
(
&
ghd
,
0
,
sizeof
(
ghd
));
for
(
i
=
0
;
i
<
lh
->
numdescs
;
i
++
)
ghd
.
values
[
i
]
=
test_bit
(
i
,
vals
);
if
(
copy_to_user
(
ip
,
&
ghd
,
sizeof
(
ghd
)))
return
-
EFAULT
;
return
0
;
}
else
if
(
cmd
==
GPIOHANDLE_SET_LINE_VALUES_IOCTL
)
{
/*
* All line descriptors were created at once with the same
* flags so just check if the first one is really output.
*/
if
(
!
test_bit
(
FLAG_IS_OUT
,
&
lh
->
descs
[
0
]
->
flags
))
return
-
EPERM
;
if
(
copy_from_user
(
&
ghd
,
ip
,
sizeof
(
ghd
)))
return
-
EFAULT
;
/* Clamp all values to [0,1] */
for
(
i
=
0
;
i
<
lh
->
numdescs
;
i
++
)
__assign_bit
(
i
,
vals
,
ghd
.
values
[
i
]);
/* Reuse the array setting function */
return
gpiod_set_array_value_complex
(
false
,
true
,
lh
->
numdescs
,
lh
->
descs
,
NULL
,
vals
);
}
else
if
(
cmd
==
GPIOHANDLE_SET_CONFIG_IOCTL
)
{
return
linehandle_set_config
(
lh
,
ip
);
}
return
-
EINVAL
;
}
#ifdef CONFIG_COMPAT
static
long
linehandle_ioctl_compat
(
struct
file
*
filep
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
return
linehandle_ioctl
(
filep
,
cmd
,
(
unsigned
long
)
compat_ptr
(
arg
));
}
#endif
static
int
linehandle_release
(
struct
inode
*
inode
,
struct
file
*
filep
)
{
struct
linehandle_state
*
lh
=
filep
->
private_data
;
struct
gpio_device
*
gdev
=
lh
->
gdev
;
int
i
;
for
(
i
=
0
;
i
<
lh
->
numdescs
;
i
++
)
gpiod_free
(
lh
->
descs
[
i
]);
kfree
(
lh
->
label
);
kfree
(
lh
);
put_device
(
&
gdev
->
dev
);
return
0
;
}
static
const
struct
file_operations
linehandle_fileops
=
{
.
release
=
linehandle_release
,
.
owner
=
THIS_MODULE
,
.
llseek
=
noop_llseek
,
.
unlocked_ioctl
=
linehandle_ioctl
,
#ifdef CONFIG_COMPAT
.
compat_ioctl
=
linehandle_ioctl_compat
,
#endif
};
static
int
linehandle_create
(
struct
gpio_device
*
gdev
,
void
__user
*
ip
)
{
struct
gpiohandle_request
handlereq
;
struct
linehandle_state
*
lh
;
struct
file
*
file
;
int
fd
,
i
,
count
=
0
,
ret
;
u32
lflags
;
if
(
copy_from_user
(
&
handlereq
,
ip
,
sizeof
(
handlereq
)))
return
-
EFAULT
;
if
((
handlereq
.
lines
==
0
)
||
(
handlereq
.
lines
>
GPIOHANDLES_MAX
))
return
-
EINVAL
;
lflags
=
handlereq
.
flags
;
ret
=
linehandle_validate_flags
(
lflags
);
if
(
ret
)
return
ret
;
lh
=
kzalloc
(
sizeof
(
*
lh
),
GFP_KERNEL
);
if
(
!
lh
)
return
-
ENOMEM
;
lh
->
gdev
=
gdev
;
get_device
(
&
gdev
->
dev
);
/* Make sure this is terminated */
handlereq
.
consumer_label
[
sizeof
(
handlereq
.
consumer_label
)
-
1
]
=
'\0'
;
if
(
strlen
(
handlereq
.
consumer_label
))
{
lh
->
label
=
kstrdup
(
handlereq
.
consumer_label
,
GFP_KERNEL
);
if
(
!
lh
->
label
)
{
ret
=
-
ENOMEM
;
goto
out_free_lh
;
}
}
/* Request each GPIO */
for
(
i
=
0
;
i
<
handlereq
.
lines
;
i
++
)
{
u32
offset
=
handlereq
.
lineoffsets
[
i
];
struct
gpio_desc
*
desc
=
gpiochip_get_desc
(
gdev
->
chip
,
offset
);
if
(
IS_ERR
(
desc
))
{
ret
=
PTR_ERR
(
desc
);
goto
out_free_descs
;
}
ret
=
gpiod_request
(
desc
,
lh
->
label
);
if
(
ret
)
goto
out_free_descs
;
lh
->
descs
[
i
]
=
desc
;
count
=
i
+
1
;
if
(
lflags
&
GPIOHANDLE_REQUEST_ACTIVE_LOW
)
set_bit
(
FLAG_ACTIVE_LOW
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_OPEN_DRAIN
)
set_bit
(
FLAG_OPEN_DRAIN
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_OPEN_SOURCE
)
set_bit
(
FLAG_OPEN_SOURCE
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_DISABLE
)
set_bit
(
FLAG_BIAS_DISABLE
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
)
set_bit
(
FLAG_PULL_DOWN
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)
set_bit
(
FLAG_PULL_UP
,
&
desc
->
flags
);
ret
=
gpiod_set_transitory
(
desc
,
false
);
if
(
ret
<
0
)
goto
out_free_descs
;
/*
* Lines have to be requested explicitly for input
* or output, else the line will be treated "as is".
*/
if
(
lflags
&
GPIOHANDLE_REQUEST_OUTPUT
)
{
int
val
=
!!
handlereq
.
default_values
[
i
];
ret
=
gpiod_direction_output
(
desc
,
val
);
if
(
ret
)
goto
out_free_descs
;
}
else
if
(
lflags
&
GPIOHANDLE_REQUEST_INPUT
)
{
ret
=
gpiod_direction_input
(
desc
);
if
(
ret
)
goto
out_free_descs
;
}
atomic_notifier_call_chain
(
&
desc
->
gdev
->
notifier
,
GPIOLINE_CHANGED_REQUESTED
,
desc
);
dev_dbg
(
&
gdev
->
dev
,
"registered chardev handle for line %d
\n
"
,
offset
);
}
/* Let i point at the last handle */
i
--
;
lh
->
numdescs
=
handlereq
.
lines
;
fd
=
get_unused_fd_flags
(
O_RDONLY
|
O_CLOEXEC
);
if
(
fd
<
0
)
{
ret
=
fd
;
goto
out_free_descs
;
}
file
=
anon_inode_getfile
(
"gpio-linehandle"
,
&
linehandle_fileops
,
lh
,
O_RDONLY
|
O_CLOEXEC
);
if
(
IS_ERR
(
file
))
{
ret
=
PTR_ERR
(
file
);
goto
out_put_unused_fd
;
}
handlereq
.
fd
=
fd
;
if
(
copy_to_user
(
ip
,
&
handlereq
,
sizeof
(
handlereq
)))
{
/*
* fput() will trigger the release() callback, so do not go onto
* the regular error cleanup path here.
*/
fput
(
file
);
put_unused_fd
(
fd
);
return
-
EFAULT
;
}
fd_install
(
fd
,
file
);
dev_dbg
(
&
gdev
->
dev
,
"registered chardev handle for %d lines
\n
"
,
lh
->
numdescs
);
return
0
;
out_put_unused_fd:
put_unused_fd
(
fd
);
out_free_descs:
for
(
i
=
0
;
i
<
count
;
i
++
)
gpiod_free
(
lh
->
descs
[
i
]);
kfree
(
lh
->
label
);
out_free_lh:
kfree
(
lh
);
put_device
(
&
gdev
->
dev
);
return
ret
;
}
/*
* GPIO line event management
*/
/**
* struct lineevent_state - contains the state of a userspace event
* @gdev: the GPIO device the event pertains to
* @label: consumer label used to tag descriptors
* @desc: the GPIO descriptor held by this event
* @eflags: the event flags this line was requested with
* @irq: the interrupt that trigger in response to events on this GPIO
* @wait: wait queue that handles blocking reads of events
* @events: KFIFO for the GPIO events
* @timestamp: cache for the timestamp storing it between hardirq
* and IRQ thread, used to bring the timestamp close to the actual
* event
*/
struct
lineevent_state
{
struct
gpio_device
*
gdev
;
const
char
*
label
;
struct
gpio_desc
*
desc
;
u32
eflags
;
int
irq
;
wait_queue_head_t
wait
;
DECLARE_KFIFO
(
events
,
struct
gpioevent_data
,
16
);
u64
timestamp
;
};
#define GPIOEVENT_REQUEST_VALID_FLAGS \
(GPIOEVENT_REQUEST_RISING_EDGE | \
GPIOEVENT_REQUEST_FALLING_EDGE)
static
__poll_t
lineevent_poll
(
struct
file
*
filep
,
struct
poll_table_struct
*
wait
)
{
struct
lineevent_state
*
le
=
filep
->
private_data
;
__poll_t
events
=
0
;
poll_wait
(
filep
,
&
le
->
wait
,
wait
);
if
(
!
kfifo_is_empty_spinlocked_noirqsave
(
&
le
->
events
,
&
le
->
wait
.
lock
))
events
=
EPOLLIN
|
EPOLLRDNORM
;
return
events
;
}
static
ssize_t
lineevent_read
(
struct
file
*
filep
,
char
__user
*
buf
,
size_t
count
,
loff_t
*
f_ps
)
{
struct
lineevent_state
*
le
=
filep
->
private_data
;
struct
gpioevent_data
ge
;
ssize_t
bytes_read
=
0
;
int
ret
;
if
(
count
<
sizeof
(
ge
))
return
-
EINVAL
;
do
{
spin_lock
(
&
le
->
wait
.
lock
);
if
(
kfifo_is_empty
(
&
le
->
events
))
{
if
(
bytes_read
)
{
spin_unlock
(
&
le
->
wait
.
lock
);
return
bytes_read
;
}
if
(
filep
->
f_flags
&
O_NONBLOCK
)
{
spin_unlock
(
&
le
->
wait
.
lock
);
return
-
EAGAIN
;
}
ret
=
wait_event_interruptible_locked
(
le
->
wait
,
!
kfifo_is_empty
(
&
le
->
events
));
if
(
ret
)
{
spin_unlock
(
&
le
->
wait
.
lock
);
return
ret
;
}
}
ret
=
kfifo_out
(
&
le
->
events
,
&
ge
,
1
);
spin_unlock
(
&
le
->
wait
.
lock
);
if
(
ret
!=
1
)
{
/*
* This should never happen - we were holding the lock
* from the moment we learned the fifo is no longer
* empty until now.
*/
ret
=
-
EIO
;
break
;
}
if
(
copy_to_user
(
buf
+
bytes_read
,
&
ge
,
sizeof
(
ge
)))
return
-
EFAULT
;
bytes_read
+=
sizeof
(
ge
);
}
while
(
count
>=
bytes_read
+
sizeof
(
ge
));
return
bytes_read
;
}
static
int
lineevent_release
(
struct
inode
*
inode
,
struct
file
*
filep
)
{
struct
lineevent_state
*
le
=
filep
->
private_data
;
struct
gpio_device
*
gdev
=
le
->
gdev
;
free_irq
(
le
->
irq
,
le
);
gpiod_free
(
le
->
desc
);
kfree
(
le
->
label
);
kfree
(
le
);
put_device
(
&
gdev
->
dev
);
return
0
;
}
static
long
lineevent_ioctl
(
struct
file
*
filep
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
lineevent_state
*
le
=
filep
->
private_data
;
void
__user
*
ip
=
(
void
__user
*
)
arg
;
struct
gpiohandle_data
ghd
;
/*
* We can get the value for an event line but not set it,
* because it is input by definition.
*/
if
(
cmd
==
GPIOHANDLE_GET_LINE_VALUES_IOCTL
)
{
int
val
;
memset
(
&
ghd
,
0
,
sizeof
(
ghd
));
val
=
gpiod_get_value_cansleep
(
le
->
desc
);
if
(
val
<
0
)
return
val
;
ghd
.
values
[
0
]
=
val
;
if
(
copy_to_user
(
ip
,
&
ghd
,
sizeof
(
ghd
)))
return
-
EFAULT
;
return
0
;
}
return
-
EINVAL
;
}
#ifdef CONFIG_COMPAT
static
long
lineevent_ioctl_compat
(
struct
file
*
filep
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
return
lineevent_ioctl
(
filep
,
cmd
,
(
unsigned
long
)
compat_ptr
(
arg
));
}
#endif
static
const
struct
file_operations
lineevent_fileops
=
{
.
release
=
lineevent_release
,
.
read
=
lineevent_read
,
.
poll
=
lineevent_poll
,
.
owner
=
THIS_MODULE
,
.
llseek
=
noop_llseek
,
.
unlocked_ioctl
=
lineevent_ioctl
,
#ifdef CONFIG_COMPAT
.
compat_ioctl
=
lineevent_ioctl_compat
,
#endif
};
static
irqreturn_t
lineevent_irq_thread
(
int
irq
,
void
*
p
)
{
struct
lineevent_state
*
le
=
p
;
struct
gpioevent_data
ge
;
int
ret
;
/* Do not leak kernel stack to userspace */
memset
(
&
ge
,
0
,
sizeof
(
ge
));
/*
* We may be running from a nested threaded interrupt in which case
* we didn't get the timestamp from lineevent_irq_handler().
*/
if
(
!
le
->
timestamp
)
ge
.
timestamp
=
ktime_get_ns
();
else
ge
.
timestamp
=
le
->
timestamp
;
if
(
le
->
eflags
&
GPIOEVENT_REQUEST_RISING_EDGE
&&
le
->
eflags
&
GPIOEVENT_REQUEST_FALLING_EDGE
)
{
int
level
=
gpiod_get_value_cansleep
(
le
->
desc
);
if
(
level
)
/* Emit low-to-high event */
ge
.
id
=
GPIOEVENT_EVENT_RISING_EDGE
;
else
/* Emit high-to-low event */
ge
.
id
=
GPIOEVENT_EVENT_FALLING_EDGE
;
}
else
if
(
le
->
eflags
&
GPIOEVENT_REQUEST_RISING_EDGE
)
{
/* Emit low-to-high event */
ge
.
id
=
GPIOEVENT_EVENT_RISING_EDGE
;
}
else
if
(
le
->
eflags
&
GPIOEVENT_REQUEST_FALLING_EDGE
)
{
/* Emit high-to-low event */
ge
.
id
=
GPIOEVENT_EVENT_FALLING_EDGE
;
}
else
{
return
IRQ_NONE
;
}
ret
=
kfifo_in_spinlocked_noirqsave
(
&
le
->
events
,
&
ge
,
1
,
&
le
->
wait
.
lock
);
if
(
ret
)
wake_up_poll
(
&
le
->
wait
,
EPOLLIN
);
else
pr_debug_ratelimited
(
"event FIFO is full - event dropped
\n
"
);
return
IRQ_HANDLED
;
}
static
irqreturn_t
lineevent_irq_handler
(
int
irq
,
void
*
p
)
{
struct
lineevent_state
*
le
=
p
;
/*
* Just store the timestamp in hardirq context so we get it as
* close in time as possible to the actual event.
*/
le
->
timestamp
=
ktime_get_ns
();
return
IRQ_WAKE_THREAD
;
}
static
int
lineevent_create
(
struct
gpio_device
*
gdev
,
void
__user
*
ip
)
{
struct
gpioevent_request
eventreq
;
struct
lineevent_state
*
le
;
struct
gpio_desc
*
desc
;
struct
file
*
file
;
u32
offset
;
u32
lflags
;
u32
eflags
;
int
fd
;
int
ret
;
int
irqflags
=
0
;
if
(
copy_from_user
(
&
eventreq
,
ip
,
sizeof
(
eventreq
)))
return
-
EFAULT
;
offset
=
eventreq
.
lineoffset
;
lflags
=
eventreq
.
handleflags
;
eflags
=
eventreq
.
eventflags
;
desc
=
gpiochip_get_desc
(
gdev
->
chip
,
offset
);
if
(
IS_ERR
(
desc
))
return
PTR_ERR
(
desc
);
/* Return an error if a unknown flag is set */
if
((
lflags
&
~
GPIOHANDLE_REQUEST_VALID_FLAGS
)
||
(
eflags
&
~
GPIOEVENT_REQUEST_VALID_FLAGS
))
return
-
EINVAL
;
/* This is just wrong: we don't look for events on output lines */
if
((
lflags
&
GPIOHANDLE_REQUEST_OUTPUT
)
||
(
lflags
&
GPIOHANDLE_REQUEST_OPEN_DRAIN
)
||
(
lflags
&
GPIOHANDLE_REQUEST_OPEN_SOURCE
))
return
-
EINVAL
;
/* Only one bias flag can be set. */
if
(((
lflags
&
GPIOHANDLE_REQUEST_BIAS_DISABLE
)
&&
(
lflags
&
(
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
|
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)))
||
((
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
)
&&
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)))
return
-
EINVAL
;
le
=
kzalloc
(
sizeof
(
*
le
),
GFP_KERNEL
);
if
(
!
le
)
return
-
ENOMEM
;
le
->
gdev
=
gdev
;
get_device
(
&
gdev
->
dev
);
/* Make sure this is terminated */
eventreq
.
consumer_label
[
sizeof
(
eventreq
.
consumer_label
)
-
1
]
=
'\0'
;
if
(
strlen
(
eventreq
.
consumer_label
))
{
le
->
label
=
kstrdup
(
eventreq
.
consumer_label
,
GFP_KERNEL
);
if
(
!
le
->
label
)
{
ret
=
-
ENOMEM
;
goto
out_free_le
;
}
}
ret
=
gpiod_request
(
desc
,
le
->
label
);
if
(
ret
)
goto
out_free_label
;
le
->
desc
=
desc
;
le
->
eflags
=
eflags
;
if
(
lflags
&
GPIOHANDLE_REQUEST_ACTIVE_LOW
)
set_bit
(
FLAG_ACTIVE_LOW
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_DISABLE
)
set_bit
(
FLAG_BIAS_DISABLE
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_DOWN
)
set_bit
(
FLAG_PULL_DOWN
,
&
desc
->
flags
);
if
(
lflags
&
GPIOHANDLE_REQUEST_BIAS_PULL_UP
)
set_bit
(
FLAG_PULL_UP
,
&
desc
->
flags
);
ret
=
gpiod_direction_input
(
desc
);
if
(
ret
)
goto
out_free_desc
;
atomic_notifier_call_chain
(
&
desc
->
gdev
->
notifier
,
GPIOLINE_CHANGED_REQUESTED
,
desc
);
le
->
irq
=
gpiod_to_irq
(
desc
);
if
(
le
->
irq
<=
0
)
{
ret
=
-
ENODEV
;
goto
out_free_desc
;
}
if
(
eflags
&
GPIOEVENT_REQUEST_RISING_EDGE
)
irqflags
|=
test_bit
(
FLAG_ACTIVE_LOW
,
&
desc
->
flags
)
?
IRQF_TRIGGER_FALLING
:
IRQF_TRIGGER_RISING
;
if
(
eflags
&
GPIOEVENT_REQUEST_FALLING_EDGE
)
irqflags
|=
test_bit
(
FLAG_ACTIVE_LOW
,
&
desc
->
flags
)
?
IRQF_TRIGGER_RISING
:
IRQF_TRIGGER_FALLING
;
irqflags
|=
IRQF_ONESHOT
;
INIT_KFIFO
(
le
->
events
);
init_waitqueue_head
(
&
le
->
wait
);
/* Request a thread to read the events */
ret
=
request_threaded_irq
(
le
->
irq
,
lineevent_irq_handler
,
lineevent_irq_thread
,
irqflags
,
le
->
label
,
le
);
if
(
ret
)
goto
out_free_desc
;
fd
=
get_unused_fd_flags
(
O_RDONLY
|
O_CLOEXEC
);
if
(
fd
<
0
)
{
ret
=
fd
;
goto
out_free_irq
;
}
file
=
anon_inode_getfile
(
"gpio-event"
,
&
lineevent_fileops
,
le
,
O_RDONLY
|
O_CLOEXEC
);
if
(
IS_ERR
(
file
))
{
ret
=
PTR_ERR
(
file
);
goto
out_put_unused_fd
;
}
eventreq
.
fd
=
fd
;
if
(
copy_to_user
(
ip
,
&
eventreq
,
sizeof
(
eventreq
)))
{
/*
* fput() will trigger the release() callback, so do not go onto
* the regular error cleanup path here.
*/
fput
(
file
);
put_unused_fd
(
fd
);
return
-
EFAULT
;
}
fd_install
(
fd
,
file
);
return
0
;
out_put_unused_fd:
put_unused_fd
(
fd
);
out_free_irq:
free_irq
(
le
->
irq
,
le
);
out_free_desc:
gpiod_free
(
le
->
desc
);
out_free_label:
kfree
(
le
->
label
);
out_free_le:
kfree
(
le
);
put_device
(
&
gdev
->
dev
);
return
ret
;
}
static
void
gpio_desc_to_lineinfo
(
struct
gpio_desc
*
desc
,
struct
gpioline_info
*
info
)
{
struct
gpio_chip
*
gc
=
desc
->
gdev
->
chip
;
bool
ok_for_pinctrl
;
unsigned
long
flags
;
/*
* This function takes a mutex so we must check this before taking
* the spinlock.
*
* FIXME: find a non-racy way to retrieve this information. Maybe a
* lock common to both frameworks?
*/
ok_for_pinctrl
=
pinctrl_gpio_can_use_line
(
gc
->
base
+
info
->
line_offset
);
spin_lock_irqsave
(
&
gpio_lock
,
flags
);
if
(
desc
->
name
)
{
strncpy
(
info
->
name
,
desc
->
name
,
sizeof
(
info
->
name
));
info
->
name
[
sizeof
(
info
->
name
)
-
1
]
=
'\0'
;
}
else
{
info
->
name
[
0
]
=
'\0'
;
}
if
(
desc
->
label
)
{
strncpy
(
info
->
consumer
,
desc
->
label
,
sizeof
(
info
->
consumer
));
info
->
consumer
[
sizeof
(
info
->
consumer
)
-
1
]
=
'\0'
;
}
else
{
info
->
consumer
[
0
]
=
'\0'
;
}
/*
* Userspace only need to know that the kernel is using this GPIO so
* it can't use it.
*/
info
->
flags
=
0
;
if
(
test_bit
(
FLAG_REQUESTED
,
&
desc
->
flags
)
||
test_bit
(
FLAG_IS_HOGGED
,
&
desc
->
flags
)
||
test_bit
(
FLAG_USED_AS_IRQ
,
&
desc
->
flags
)
||
test_bit
(
FLAG_EXPORT
,
&
desc
->
flags
)
||
test_bit
(
FLAG_SYSFS
,
&
desc
->
flags
)
||
!
ok_for_pinctrl
)
info
->
flags
|=
GPIOLINE_FLAG_KERNEL
;
if
(
test_bit
(
FLAG_IS_OUT
,
&
desc
->
flags
))
info
->
flags
|=
GPIOLINE_FLAG_IS_OUT
;
if
(
test_bit
(
FLAG_ACTIVE_LOW
,
&
desc
->
flags
))
info
->
flags
|=
GPIOLINE_FLAG_ACTIVE_LOW
;
if
(
test_bit
(
FLAG_OPEN_DRAIN
,
&
desc
->
flags
))
info
->
flags
|=
(
GPIOLINE_FLAG_OPEN_DRAIN
|
GPIOLINE_FLAG_IS_OUT
);
if
(
test_bit
(
FLAG_OPEN_SOURCE
,
&
desc
->
flags
))
info
->
flags
|=
(
GPIOLINE_FLAG_OPEN_SOURCE
|
GPIOLINE_FLAG_IS_OUT
);
if
(
test_bit
(
FLAG_BIAS_DISABLE
,
&
desc
->
flags
))
info
->
flags
|=
GPIOLINE_FLAG_BIAS_DISABLE
;
if
(
test_bit
(
FLAG_PULL_DOWN
,
&
desc
->
flags
))
info
->
flags
|=
GPIOLINE_FLAG_BIAS_PULL_DOWN
;
if
(
test_bit
(
FLAG_PULL_UP
,
&
desc
->
flags
))
info
->
flags
|=
GPIOLINE_FLAG_BIAS_PULL_UP
;
spin_unlock_irqrestore
(
&
gpio_lock
,
flags
);
}
struct
gpio_chardev_data
{
struct
gpio_device
*
gdev
;
wait_queue_head_t
wait
;
DECLARE_KFIFO
(
events
,
struct
gpioline_info_changed
,
32
);
struct
notifier_block
lineinfo_changed_nb
;
unsigned
long
*
watched_lines
;
};
/*
* gpio_ioctl() - ioctl handler for the GPIO chardev
*/
static
long
gpio_ioctl
(
struct
file
*
filp
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
struct
gpio_chardev_data
*
priv
=
filp
->
private_data
;
struct
gpio_device
*
gdev
=
priv
->
gdev
;
struct
gpio_chip
*
gc
=
gdev
->
chip
;
void
__user
*
ip
=
(
void
__user
*
)
arg
;
struct
gpio_desc
*
desc
;
__u32
offset
;
int
hwgpio
;
/* We fail any subsequent ioctl():s when the chip is gone */
if
(
!
gc
)
return
-
ENODEV
;
/* Fill in the struct and pass to userspace */
if
(
cmd
==
GPIO_GET_CHIPINFO_IOCTL
)
{
struct
gpiochip_info
chipinfo
;
memset
(
&
chipinfo
,
0
,
sizeof
(
chipinfo
));
strncpy
(
chipinfo
.
name
,
dev_name
(
&
gdev
->
dev
),
sizeof
(
chipinfo
.
name
));
chipinfo
.
name
[
sizeof
(
chipinfo
.
name
)
-
1
]
=
'\0'
;
strncpy
(
chipinfo
.
label
,
gdev
->
label
,
sizeof
(
chipinfo
.
label
));
chipinfo
.
label
[
sizeof
(
chipinfo
.
label
)
-
1
]
=
'\0'
;
chipinfo
.
lines
=
gdev
->
ngpio
;
if
(
copy_to_user
(
ip
,
&
chipinfo
,
sizeof
(
chipinfo
)))
return
-
EFAULT
;
return
0
;
}
else
if
(
cmd
==
GPIO_GET_LINEINFO_IOCTL
)
{
struct
gpioline_info
lineinfo
;
if
(
copy_from_user
(
&
lineinfo
,
ip
,
sizeof
(
lineinfo
)))
return
-
EFAULT
;
desc
=
gpiochip_get_desc
(
gc
,
lineinfo
.
line_offset
);
if
(
IS_ERR
(
desc
))
return
PTR_ERR
(
desc
);
hwgpio
=
gpio_chip_hwgpio
(
desc
);
gpio_desc_to_lineinfo
(
desc
,
&
lineinfo
);
if
(
copy_to_user
(
ip
,
&
lineinfo
,
sizeof
(
lineinfo
)))
return
-
EFAULT
;
return
0
;
}
else
if
(
cmd
==
GPIO_GET_LINEHANDLE_IOCTL
)
{
return
linehandle_create
(
gdev
,
ip
);
}
else
if
(
cmd
==
GPIO_GET_LINEEVENT_IOCTL
)
{
return
lineevent_create
(
gdev
,
ip
);
}
else
if
(
cmd
==
GPIO_GET_LINEINFO_WATCH_IOCTL
)
{
struct
gpioline_info
lineinfo
;
if
(
copy_from_user
(
&
lineinfo
,
ip
,
sizeof
(
lineinfo
)))
return
-
EFAULT
;
desc
=
gpiochip_get_desc
(
gc
,
lineinfo
.
line_offset
);
if
(
IS_ERR
(
desc
))
return
PTR_ERR
(
desc
);
hwgpio
=
gpio_chip_hwgpio
(
desc
);
if
(
test_bit
(
hwgpio
,
priv
->
watched_lines
))
return
-
EBUSY
;
gpio_desc_to_lineinfo
(
desc
,
&
lineinfo
);
if
(
copy_to_user
(
ip
,
&
lineinfo
,
sizeof
(
lineinfo
)))
return
-
EFAULT
;
set_bit
(
hwgpio
,
priv
->
watched_lines
);
return
0
;
}
else
if
(
cmd
==
GPIO_GET_LINEINFO_UNWATCH_IOCTL
)
{
if
(
copy_from_user
(
&
offset
,
ip
,
sizeof
(
offset
)))
return
-
EFAULT
;
desc
=
gpiochip_get_desc
(
gc
,
offset
);
if
(
IS_ERR
(
desc
))
return
PTR_ERR
(
desc
);
hwgpio
=
gpio_chip_hwgpio
(
desc
);
if
(
!
test_bit
(
hwgpio
,
priv
->
watched_lines
))
return
-
EBUSY
;
clear_bit
(
hwgpio
,
priv
->
watched_lines
);
return
0
;
}
return
-
EINVAL
;
}
#ifdef CONFIG_COMPAT
static
long
gpio_ioctl_compat
(
struct
file
*
filp
,
unsigned
int
cmd
,
unsigned
long
arg
)
{
return
gpio_ioctl
(
filp
,
cmd
,
(
unsigned
long
)
compat_ptr
(
arg
));
}
#endif
static
struct
gpio_chardev_data
*
to_gpio_chardev_data
(
struct
notifier_block
*
nb
)
{
return
container_of
(
nb
,
struct
gpio_chardev_data
,
lineinfo_changed_nb
);
}
static
int
lineinfo_changed_notify
(
struct
notifier_block
*
nb
,
unsigned
long
action
,
void
*
data
)
{
struct
gpio_chardev_data
*
priv
=
to_gpio_chardev_data
(
nb
);
struct
gpioline_info_changed
chg
;
struct
gpio_desc
*
desc
=
data
;
int
ret
;
if
(
!
test_bit
(
gpio_chip_hwgpio
(
desc
),
priv
->
watched_lines
))
return
NOTIFY_DONE
;
memset
(
&
chg
,
0
,
sizeof
(
chg
));
chg
.
info
.
line_offset
=
gpio_chip_hwgpio
(
desc
);
chg
.
event_type
=
action
;
chg
.
timestamp
=
ktime_get_ns
();
gpio_desc_to_lineinfo
(
desc
,
&
chg
.
info
);
ret
=
kfifo_in_spinlocked
(
&
priv
->
events
,
&
chg
,
1
,
&
priv
->
wait
.
lock
);
if
(
ret
)
wake_up_poll
(
&
priv
->
wait
,
EPOLLIN
);
else
pr_debug_ratelimited
(
"lineinfo event FIFO is full - event dropped
\n
"
);
return
NOTIFY_OK
;
}
static
__poll_t
lineinfo_watch_poll
(
struct
file
*
filep
,
struct
poll_table_struct
*
pollt
)
{
struct
gpio_chardev_data
*
priv
=
filep
->
private_data
;
__poll_t
events
=
0
;
poll_wait
(
filep
,
&
priv
->
wait
,
pollt
);
if
(
!
kfifo_is_empty_spinlocked_noirqsave
(
&
priv
->
events
,
&
priv
->
wait
.
lock
))
events
=
EPOLLIN
|
EPOLLRDNORM
;
return
events
;
}
static
ssize_t
lineinfo_watch_read
(
struct
file
*
filep
,
char
__user
*
buf
,
size_t
count
,
loff_t
*
off
)
{
struct
gpio_chardev_data
*
priv
=
filep
->
private_data
;
struct
gpioline_info_changed
event
;
ssize_t
bytes_read
=
0
;
int
ret
;
if
(
count
<
sizeof
(
event
))
return
-
EINVAL
;
do
{
spin_lock
(
&
priv
->
wait
.
lock
);
if
(
kfifo_is_empty
(
&
priv
->
events
))
{
if
(
bytes_read
)
{
spin_unlock
(
&
priv
->
wait
.
lock
);
return
bytes_read
;
}
if
(
filep
->
f_flags
&
O_NONBLOCK
)
{
spin_unlock
(
&
priv
->
wait
.
lock
);
return
-
EAGAIN
;
}
ret
=
wait_event_interruptible_locked
(
priv
->
wait
,
!
kfifo_is_empty
(
&
priv
->
events
));
if
(
ret
)
{
spin_unlock
(
&
priv
->
wait
.
lock
);
return
ret
;
}
}
ret
=
kfifo_out
(
&
priv
->
events
,
&
event
,
1
);
spin_unlock
(
&
priv
->
wait
.
lock
);
if
(
ret
!=
1
)
{
ret
=
-
EIO
;
break
;
/* We should never get here. See lineevent_read(). */
}
if
(
copy_to_user
(
buf
+
bytes_read
,
&
event
,
sizeof
(
event
)))
return
-
EFAULT
;
bytes_read
+=
sizeof
(
event
);
}
while
(
count
>=
bytes_read
+
sizeof
(
event
));
return
bytes_read
;
}
/**
* gpio_chrdev_open() - open the chardev for ioctl operations
* @inode: inode for this chardev
* @filp: file struct for storing private data
* Returns 0 on success
*/
static
int
gpio_chrdev_open
(
struct
inode
*
inode
,
struct
file
*
filp
)
{
struct
gpio_device
*
gdev
=
container_of
(
inode
->
i_cdev
,
struct
gpio_device
,
chrdev
);
struct
gpio_chardev_data
*
priv
;
int
ret
=
-
ENOMEM
;
/* Fail on open if the backing gpiochip is gone */
if
(
!
gdev
->
chip
)
return
-
ENODEV
;
priv
=
kzalloc
(
sizeof
(
*
priv
),
GFP_KERNEL
);
if
(
!
priv
)
return
-
ENOMEM
;
priv
->
watched_lines
=
bitmap_zalloc
(
gdev
->
chip
->
ngpio
,
GFP_KERNEL
);
if
(
!
priv
->
watched_lines
)
goto
out_free_priv
;
init_waitqueue_head
(
&
priv
->
wait
);
INIT_KFIFO
(
priv
->
events
);
priv
->
gdev
=
gdev
;
priv
->
lineinfo_changed_nb
.
notifier_call
=
lineinfo_changed_notify
;
ret
=
atomic_notifier_chain_register
(
&
gdev
->
notifier
,
&
priv
->
lineinfo_changed_nb
);
if
(
ret
)
goto
out_free_bitmap
;
get_device
(
&
gdev
->
dev
);
filp
->
private_data
=
priv
;
ret
=
nonseekable_open
(
inode
,
filp
);
if
(
ret
)
goto
out_unregister_notifier
;
return
ret
;
out_unregister_notifier:
atomic_notifier_chain_unregister
(
&
gdev
->
notifier
,
&
priv
->
lineinfo_changed_nb
);
out_free_bitmap:
bitmap_free
(
priv
->
watched_lines
);
out_free_priv:
kfree
(
priv
);
return
ret
;
}
/**
* gpio_chrdev_release() - close chardev after ioctl operations
* @inode: inode for this chardev
* @filp: file struct for storing private data
* Returns 0 on success
*/
static
int
gpio_chrdev_release
(
struct
inode
*
inode
,
struct
file
*
filp
)
{
struct
gpio_chardev_data
*
priv
=
filp
->
private_data
;
struct
gpio_device
*
gdev
=
priv
->
gdev
;
bitmap_free
(
priv
->
watched_lines
);
atomic_notifier_chain_unregister
(
&
gdev
->
notifier
,
&
priv
->
lineinfo_changed_nb
);
put_device
(
&
gdev
->
dev
);
kfree
(
priv
);
return
0
;
}
static
const
struct
file_operations
gpio_fileops
=
{
.
release
=
gpio_chrdev_release
,
.
open
=
gpio_chrdev_open
,
.
poll
=
lineinfo_watch_poll
,
.
read
=
lineinfo_watch_read
,
.
owner
=
THIS_MODULE
,
.
llseek
=
no_llseek
,
.
unlocked_ioctl
=
gpio_ioctl
,
#ifdef CONFIG_COMPAT
.
compat_ioctl
=
gpio_ioctl_compat
,
#endif
};
static
void
gpiodevice_release
(
struct
device
*
dev
)
{
struct
gpio_device
*
gdev
=
dev_get_drvdata
(
dev
);
...
...
@@ -1539,17 +435,10 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
{
int
ret
;
cdev_init
(
&
gdev
->
chrdev
,
&
gpio_fileops
);
gdev
->
chrdev
.
owner
=
THIS_MODULE
;
gdev
->
dev
.
devt
=
MKDEV
(
MAJOR
(
gpio_devt
),
gdev
->
id
);
ret
=
cdev_device_add
(
&
gdev
->
chrdev
,
&
gdev
->
dev
);
ret
=
gpiolib_cdev_register
(
gdev
,
gpio_devt
);
if
(
ret
)
return
ret
;
chip_dbg
(
gdev
->
chip
,
"added GPIO chardev (%d:%d)
\n
"
,
MAJOR
(
gpio_devt
),
gdev
->
id
);
ret
=
gpiochip_sysfs_register
(
gdev
);
if
(
ret
)
goto
err_remove_device
;
...
...
@@ -1562,7 +451,7 @@ static int gpiochip_setup_dev(struct gpio_device *gdev)
return
0
;
err_remove_device:
cdev_device_del
(
&
gdev
->
chrdev
,
&
gdev
->
dev
);
gpiolib_cdev_unregister
(
g
dev
);
return
ret
;
}
...
...
@@ -1884,7 +773,7 @@ void gpiochip_remove(struct gpio_chip *gc)
* be removed, else it will be dangling until the last user is
* gone.
*/
cdev_device_del
(
&
gdev
->
chrdev
,
&
gdev
->
dev
);
gpiolib_cdev_unregister
(
g
dev
);
put_device
(
&
gdev
->
dev
);
}
EXPORT_SYMBOL_GPL
(
gpiochip_remove
);
...
...
@@ -3705,10 +2594,9 @@ int gpiod_get_array_value_complex(bool raw, bool can_sleep,
bitmap_xor
(
value_bitmap
,
value_bitmap
,
array_info
->
invert_mask
,
array_size
);
if
(
bitmap_full
(
array_info
->
get_mask
,
array_size
))
return
0
;
i
=
find_first_zero_bit
(
array_info
->
get_mask
,
array_size
);
if
(
i
==
array_size
)
return
0
;
}
else
{
array_info
=
NULL
;
}
...
...
@@ -3989,10 +2877,9 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
gpio_chip_set_multiple
(
array_info
->
chip
,
array_info
->
set_mask
,
value_bitmap
);
if
(
bitmap_full
(
array_info
->
set_mask
,
array_size
))
return
0
;
i
=
find_first_zero_bit
(
array_info
->
set_mask
,
array_size
);
if
(
i
==
array_size
)
return
0
;
}
else
{
array_info
=
NULL
;
}
...
...
drivers/pinctrl/pinctrl-at91.c
View file @
4672a4a9
...
...
@@ -1486,14 +1486,11 @@ static void at91_gpio_dbg_show(struct seq_file *s, struct gpio_chip *chip)
int
i
;
struct
at91_gpio_chip
*
at91_gpio
=
gpiochip_get_data
(
chip
);
void
__iomem
*
pio
=
at91_gpio
->
regbase
;
const
char
*
gpio_label
;
for
(
i
=
0
;
i
<
chip
->
ngpio
;
i
++
)
{
for
_each_requested_gpio
(
chip
,
i
,
gpio_label
)
{
unsigned
mask
=
pin_to_mask
(
i
);
const
char
*
gpio_label
;
gpio_label
=
gpiochip_is_requested
(
chip
,
i
);
if
(
!
gpio_label
)
continue
;
mode
=
at91_gpio
->
ops
->
get_periph
(
pio
,
mask
);
seq_printf
(
s
,
"[%s] GPIO%s%d: "
,
gpio_label
,
chip
->
label
,
i
);
...
...
include/linux/gpio/driver.h
View file @
4672a4a9
...
...
@@ -474,6 +474,22 @@ struct gpio_chip {
extern
const
char
*
gpiochip_is_requested
(
struct
gpio_chip
*
gc
,
unsigned
int
offset
);
/**
* for_each_requested_gpio_in_range - iterates over requested GPIOs in a given range
* @chip: the chip to query
* @i: loop variable
* @base: first GPIO in the range
* @size: amount of GPIOs to check starting from @base
* @label: label of current GPIO
*/
#define for_each_requested_gpio_in_range(chip, i, base, size, label) \
for (i = 0; i < size; i++) \
if ((label = gpiochip_is_requested(chip, base + i)) == NULL) {} else
/* Iterates over all requested GPIO of the given @chip */
#define for_each_requested_gpio(chip, i, label) \
for_each_requested_gpio_in_range(chip, i, 0, chip->ngpio, label)
/* add/remove chips */
extern
int
gpiochip_add_data_with_key
(
struct
gpio_chip
*
gc
,
void
*
data
,
struct
lock_class_key
*
lock_key
,
...
...
@@ -481,7 +497,7 @@ extern int gpiochip_add_data_with_key(struct gpio_chip *gc, void *data,
/**
* gpiochip_add_data() - register a gpio_chip
* @
chip
: the chip to register, with chip->base initialized
* @
gc
: the chip to register, with chip->base initialized
* @data: driver-private data associated with this chip
*
* Context: potentially before irqs will work
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment