You've already forked linux-apfs
mirror of
https://github.com/linux-apfs/linux-apfs.git
synced 2026-05-01 15:00:59 -07:00
Merge branch 'device-properties'
* device-properties: leds: leds-gpio: Fix multiple instances registration without 'label' property leds: leds-gpio: Fix legacy GPIO number case ACPI / property: Drop size_prop from acpi_dev_get_property_reference() leds: leds-gpio: Convert gpio_blink_set() to use GPIO descriptors ACPI / GPIO: Document ACPI GPIO mappings API net: rfkill: gpio: Add default GPIO driver mappings for ACPI ACPI / GPIO: Driver GPIO mappings for ACPI GPIOs input: gpio_keys_polled: Make use of device property API leds: leds-gpio: Make use of device property API gpio: Support for unified device properties interface Driver core: Unified interface for firmware node properties input: gpio_keys_polled: Add support for GPIO descriptors leds: leds-gpio: Add support for GPIO descriptors gpio: sch: Consolidate core and resume banks gpio / ACPI: Add support for _DSD device properties misc: at25: Make use of device property API ACPI: Allow drivers to match using Device Tree compatible property Driver core: Unified device properties interface for platform firmware ACPI: Add support for device specific properties
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
_DSD Device Properties Related to GPIO
|
||||
--------------------------------------
|
||||
|
||||
With the release of ACPI 5.1 and the _DSD configuration objecte names
|
||||
can finally be given to GPIOs (and other things as well) returned by
|
||||
_CRS. Previously, we were only able to use an integer index to find
|
||||
the corresponding GPIO, which is pretty error prone (it depends on
|
||||
the _CRS output ordering, for example).
|
||||
|
||||
With _DSD we can now query GPIOs using a name instead of an integer
|
||||
index, like the ASL example below shows:
|
||||
|
||||
// Bluetooth device with reset and shutdown GPIOs
|
||||
Device (BTH)
|
||||
{
|
||||
Name (_HID, ...)
|
||||
|
||||
Name (_CRS, ResourceTemplate ()
|
||||
{
|
||||
GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionInputOnly,
|
||||
"\\_SB.GPO0", 0, ResourceConsumer) {15}
|
||||
GpioIo (Exclusive, PullUp, 0, 0, IoRestrictionInputOnly,
|
||||
"\\_SB.GPO0", 0, ResourceConsumer) {27, 31}
|
||||
})
|
||||
|
||||
Name (_DSD, Package ()
|
||||
{
|
||||
ToUUID("daffd814-6eba-4d8c-8a91-bc9bbf4aa301"),
|
||||
Package ()
|
||||
{
|
||||
Package () {"reset-gpio", Package() {^BTH, 1, 1, 0 }},
|
||||
Package () {"shutdown-gpio", Package() {^BTH, 0, 0, 0 }},
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
The format of the supported GPIO property is:
|
||||
|
||||
Package () { "name", Package () { ref, index, pin, active_low }}
|
||||
|
||||
ref - The device that has _CRS containing GpioIo()/GpioInt() resources,
|
||||
typically this is the device itself (BTH in our case).
|
||||
index - Index of the GpioIo()/GpioInt() resource in _CRS starting from zero.
|
||||
pin - Pin in the GpioIo()/GpioInt() resource. Typically this is zero.
|
||||
active_low - If 1 the GPIO is marked as active_low.
|
||||
|
||||
Since ACPI GpioIo() resource does not have a field saying whether it is
|
||||
active low or high, the "active_low" argument can be used here. Setting
|
||||
it to 1 marks the GPIO as active low.
|
||||
|
||||
In our Bluetooth example the "reset-gpio" refers to the second GpioIo()
|
||||
resource, second pin in that resource with the GPIO number of 31.
|
||||
|
||||
ACPI GPIO Mappings Provided by Drivers
|
||||
--------------------------------------
|
||||
|
||||
There are systems in which the ACPI tables do not contain _DSD but provide _CRS
|
||||
with GpioIo()/GpioInt() resources and device drivers still need to work with
|
||||
them.
|
||||
|
||||
In those cases ACPI device identification objects, _HID, _CID, _CLS, _SUB, _HRV,
|
||||
available to the driver can be used to identify the device and that is supposed
|
||||
to be sufficient to determine the meaning and purpose of all of the GPIO lines
|
||||
listed by the GpioIo()/GpioInt() resources returned by _CRS. In other words,
|
||||
the driver is supposed to know what to use the GpioIo()/GpioInt() resources for
|
||||
once it has identified the device. Having done that, it can simply assign names
|
||||
to the GPIO lines it is going to use and provide the GPIO subsystem with a
|
||||
mapping between those names and the ACPI GPIO resources corresponding to them.
|
||||
|
||||
To do that, the driver needs to define a mapping table as a NULL-terminated
|
||||
array of struct acpi_gpio_mapping objects that each contain a name, a pointer
|
||||
to an array of line data (struct acpi_gpio_params) objects and the size of that
|
||||
array. Each struct acpi_gpio_params object consists of three fields,
|
||||
crs_entry_index, line_index, active_low, representing the index of the target
|
||||
GpioIo()/GpioInt() resource in _CRS starting from zero, the index of the target
|
||||
line in that resource starting from zero, and the active-low flag for that line,
|
||||
respectively, in analogy with the _DSD GPIO property format specified above.
|
||||
|
||||
For the example Bluetooth device discussed previously the data structures in
|
||||
question would look like this:
|
||||
|
||||
static const struct acpi_gpio_params reset_gpio = { 1, 1, false };
|
||||
static const struct acpi_gpio_params shutdown_gpio = { 0, 0, false };
|
||||
|
||||
static const struct acpi_gpio_mapping bluetooth_acpi_gpios[] = {
|
||||
{ "reset-gpio", &reset_gpio, 1 },
|
||||
{ "shutdown-gpio", &shutdown_gpio, 1 },
|
||||
{ },
|
||||
};
|
||||
|
||||
Next, the mapping table needs to be passed as the second argument to
|
||||
acpi_dev_add_driver_gpios() that will register it with the ACPI device object
|
||||
pointed to by its first argument. That should be done in the driver's .probe()
|
||||
routine. On removal, the driver should unregister its GPIO mapping table by
|
||||
calling acpi_dev_remove_driver_gpios() on the ACPI device object where that
|
||||
table was previously registered.
|
||||
@@ -219,6 +219,24 @@ part of the IRQ interface, e.g. IRQF_TRIGGER_FALLING, as are system wakeup
|
||||
capabilities.
|
||||
|
||||
|
||||
GPIOs and ACPI
|
||||
==============
|
||||
|
||||
On ACPI systems, GPIOs are described by GpioIo()/GpioInt() resources listed by
|
||||
the _CRS configuration objects of devices. Those resources do not provide
|
||||
connection IDs (names) for GPIOs, so it is necessary to use an additional
|
||||
mechanism for this purpose.
|
||||
|
||||
Systems compliant with ACPI 5.1 or newer may provide a _DSD configuration object
|
||||
which, among other things, may be used to provide connection IDs for specific
|
||||
GPIOs described by the GpioIo()/GpioInt() resources in _CRS. If that is the
|
||||
case, it will be handled by the GPIO subsystem automatically. However, if the
|
||||
_DSD is not present, the mappings between GpioIo()/GpioInt() resources and GPIO
|
||||
connection IDs need to be provided by device drivers.
|
||||
|
||||
For details refer to Documentation/acpi/gpio-properties.txt
|
||||
|
||||
|
||||
Interacting With the Legacy GPIO Subsystem
|
||||
==========================================
|
||||
Many kernel subsystems still handle GPIOs using the legacy integer-based
|
||||
|
||||
@@ -41,7 +41,7 @@ static void h1940bt_enable(int on)
|
||||
mdelay(10);
|
||||
gpio_set_value(S3C2410_GPH(1), 0);
|
||||
|
||||
h1940_led_blink_set(-EINVAL, GPIO_LED_BLINK, NULL, NULL);
|
||||
h1940_led_blink_set(NULL, GPIO_LED_BLINK, NULL, NULL);
|
||||
}
|
||||
else {
|
||||
gpio_set_value(S3C2410_GPH(1), 1);
|
||||
@@ -50,7 +50,7 @@ static void h1940bt_enable(int on)
|
||||
mdelay(10);
|
||||
gpio_set_value(H1940_LATCH_BLUETOOTH_POWER, 0);
|
||||
|
||||
h1940_led_blink_set(-EINVAL, GPIO_LED_NO_BLINK_LOW, NULL, NULL);
|
||||
h1940_led_blink_set(NULL, GPIO_LED_NO_BLINK_LOW, NULL, NULL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -19,8 +19,10 @@
|
||||
#define H1940_SUSPEND_RESUMEAT (0x30081000)
|
||||
#define H1940_SUSPEND_CHECK (0x30080000)
|
||||
|
||||
struct gpio_desc;
|
||||
|
||||
extern void h1940_pm_return(void);
|
||||
extern int h1940_led_blink_set(unsigned gpio, int state,
|
||||
extern int h1940_led_blink_set(struct gpio_desc *desc, int state,
|
||||
unsigned long *delay_on,
|
||||
unsigned long *delay_off);
|
||||
|
||||
|
||||
@@ -359,10 +359,11 @@ static struct platform_device h1940_battery = {
|
||||
|
||||
static DEFINE_SPINLOCK(h1940_blink_spin);
|
||||
|
||||
int h1940_led_blink_set(unsigned gpio, int state,
|
||||
int h1940_led_blink_set(struct gpio_desc *desc, int state,
|
||||
unsigned long *delay_on, unsigned long *delay_off)
|
||||
{
|
||||
int blink_gpio, check_gpio1, check_gpio2;
|
||||
int gpio = desc ? desc_to_gpio(desc) : -EINVAL;
|
||||
|
||||
switch (gpio) {
|
||||
case H1940_LATCH_LED_GREEN:
|
||||
|
||||
@@ -250,9 +250,10 @@ static void rx1950_disable_charger(void)
|
||||
|
||||
static DEFINE_SPINLOCK(rx1950_blink_spin);
|
||||
|
||||
static int rx1950_led_blink_set(unsigned gpio, int state,
|
||||
static int rx1950_led_blink_set(struct gpio_desc *desc, int state,
|
||||
unsigned long *delay_on, unsigned long *delay_off)
|
||||
{
|
||||
int gpio = desc_to_gpio(desc);
|
||||
int blink_gpio, check_gpio;
|
||||
|
||||
switch (gpio) {
|
||||
|
||||
@@ -306,9 +306,10 @@ EXPORT_SYMBOL(orion_gpio_set_blink);
|
||||
|
||||
#define ORION_BLINK_HALF_PERIOD 100 /* ms */
|
||||
|
||||
int orion_gpio_led_blink_set(unsigned gpio, int state,
|
||||
int orion_gpio_led_blink_set(struct gpio_desc *desc, int state,
|
||||
unsigned long *delay_on, unsigned long *delay_off)
|
||||
{
|
||||
unsigned gpio = desc_to_gpio(desc);
|
||||
|
||||
if (delay_on && delay_off && !*delay_on && !*delay_off)
|
||||
*delay_on = *delay_off = ORION_BLINK_HALF_PERIOD;
|
||||
|
||||
@@ -14,12 +14,15 @@
|
||||
#include <linux/init.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/irqdomain.h>
|
||||
|
||||
struct gpio_desc;
|
||||
|
||||
/*
|
||||
* Orion-specific GPIO API extensions.
|
||||
*/
|
||||
void orion_gpio_set_unused(unsigned pin);
|
||||
void orion_gpio_set_blink(unsigned pin, int blink);
|
||||
int orion_gpio_led_blink_set(unsigned gpio, int state,
|
||||
int orion_gpio_led_blink_set(struct gpio_desc *desc, int state,
|
||||
unsigned long *delay_on, unsigned long *delay_off);
|
||||
|
||||
#define GPIO_INPUT_OK (1 << 0)
|
||||
|
||||
@@ -47,6 +47,7 @@ acpi-y += int340x_thermal.o
|
||||
acpi-y += power.o
|
||||
acpi-y += event.o
|
||||
acpi-y += sysfs.o
|
||||
acpi-y += property.o
|
||||
acpi-$(CONFIG_X86) += acpi_cmos_rtc.o
|
||||
acpi-$(CONFIG_DEBUG_FS) += debugfs.o
|
||||
acpi-$(CONFIG_ACPI_NUMA) += numa.o
|
||||
|
||||
@@ -173,4 +173,10 @@ static inline void suspend_nvs_restore(void) {}
|
||||
bool acpi_osi_is_win8(void);
|
||||
#endif
|
||||
|
||||
/*--------------------------------------------------------------------------
|
||||
Device properties
|
||||
-------------------------------------------------------------------------- */
|
||||
void acpi_init_properties(struct acpi_device *adev);
|
||||
void acpi_free_properties(struct acpi_device *adev);
|
||||
|
||||
#endif /* _ACPI_INTERNAL_H_ */
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
+117
-10
@@ -124,17 +124,56 @@ static int create_modalias(struct acpi_device *acpi_dev, char *modalias,
|
||||
if (list_empty(&acpi_dev->pnp.ids))
|
||||
return 0;
|
||||
|
||||
len = snprintf(modalias, size, "acpi:");
|
||||
size -= len;
|
||||
/*
|
||||
* If the device has PRP0001 we expose DT compatible modalias
|
||||
* instead in form of of:NnameTCcompatible.
|
||||
*/
|
||||
if (acpi_dev->data.of_compatible) {
|
||||
struct acpi_buffer buf = { ACPI_ALLOCATE_BUFFER };
|
||||
const union acpi_object *of_compatible, *obj;
|
||||
int i, nval;
|
||||
char *c;
|
||||
|
||||
list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
|
||||
count = snprintf(&modalias[len], size, "%s:", id->id);
|
||||
if (count < 0)
|
||||
return -EINVAL;
|
||||
if (count >= size)
|
||||
return -ENOMEM;
|
||||
len += count;
|
||||
size -= count;
|
||||
acpi_get_name(acpi_dev->handle, ACPI_SINGLE_NAME, &buf);
|
||||
/* DT strings are all in lower case */
|
||||
for (c = buf.pointer; *c != '\0'; c++)
|
||||
*c = tolower(*c);
|
||||
|
||||
len = snprintf(modalias, size, "of:N%sT", (char *)buf.pointer);
|
||||
ACPI_FREE(buf.pointer);
|
||||
|
||||
of_compatible = acpi_dev->data.of_compatible;
|
||||
if (of_compatible->type == ACPI_TYPE_PACKAGE) {
|
||||
nval = of_compatible->package.count;
|
||||
obj = of_compatible->package.elements;
|
||||
} else { /* Must be ACPI_TYPE_STRING. */
|
||||
nval = 1;
|
||||
obj = of_compatible;
|
||||
}
|
||||
for (i = 0; i < nval; i++, obj++) {
|
||||
count = snprintf(&modalias[len], size, "C%s",
|
||||
obj->string.pointer);
|
||||
if (count < 0)
|
||||
return -EINVAL;
|
||||
if (count >= size)
|
||||
return -ENOMEM;
|
||||
|
||||
len += count;
|
||||
size -= count;
|
||||
}
|
||||
} else {
|
||||
len = snprintf(modalias, size, "acpi:");
|
||||
size -= len;
|
||||
|
||||
list_for_each_entry(id, &acpi_dev->pnp.ids, list) {
|
||||
count = snprintf(&modalias[len], size, "%s:", id->id);
|
||||
if (count < 0)
|
||||
return -EINVAL;
|
||||
if (count >= size)
|
||||
return -ENOMEM;
|
||||
len += count;
|
||||
size -= count;
|
||||
}
|
||||
}
|
||||
|
||||
modalias[len] = '\0';
|
||||
@@ -902,6 +941,51 @@ int acpi_match_device_ids(struct acpi_device *device,
|
||||
}
|
||||
EXPORT_SYMBOL(acpi_match_device_ids);
|
||||
|
||||
/* Performs match against special "PRP0001" shoehorn ACPI ID */
|
||||
static bool acpi_of_driver_match_device(struct device *dev,
|
||||
const struct device_driver *drv)
|
||||
{
|
||||
const union acpi_object *of_compatible, *obj;
|
||||
struct acpi_device *adev;
|
||||
int i, nval;
|
||||
|
||||
adev = ACPI_COMPANION(dev);
|
||||
if (!adev)
|
||||
return false;
|
||||
|
||||
of_compatible = adev->data.of_compatible;
|
||||
if (!drv->of_match_table || !of_compatible)
|
||||
return false;
|
||||
|
||||
if (of_compatible->type == ACPI_TYPE_PACKAGE) {
|
||||
nval = of_compatible->package.count;
|
||||
obj = of_compatible->package.elements;
|
||||
} else { /* Must be ACPI_TYPE_STRING. */
|
||||
nval = 1;
|
||||
obj = of_compatible;
|
||||
}
|
||||
/* Now we can look for the driver DT compatible strings */
|
||||
for (i = 0; i < nval; i++, obj++) {
|
||||
const struct of_device_id *id;
|
||||
|
||||
for (id = drv->of_match_table; id->compatible[0]; id++)
|
||||
if (!strcasecmp(obj->string.pointer, id->compatible))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool acpi_driver_match_device(struct device *dev,
|
||||
const struct device_driver *drv)
|
||||
{
|
||||
if (!drv->acpi_match_table)
|
||||
return acpi_of_driver_match_device(dev, drv);
|
||||
|
||||
return !!acpi_match_device(drv->acpi_match_table, dev);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_driver_match_device);
|
||||
|
||||
static void acpi_free_power_resources_lists(struct acpi_device *device)
|
||||
{
|
||||
int i;
|
||||
@@ -922,6 +1006,7 @@ static void acpi_device_release(struct device *dev)
|
||||
{
|
||||
struct acpi_device *acpi_dev = to_acpi_device(dev);
|
||||
|
||||
acpi_free_properties(acpi_dev);
|
||||
acpi_free_pnp_ids(&acpi_dev->pnp);
|
||||
acpi_free_power_resources_lists(acpi_dev);
|
||||
kfree(acpi_dev);
|
||||
@@ -1304,6 +1389,26 @@ int acpi_device_add(struct acpi_device *device,
|
||||
return result;
|
||||
}
|
||||
|
||||
struct acpi_device *acpi_get_next_child(struct device *dev,
|
||||
struct acpi_device *child)
|
||||
{
|
||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||
struct list_head *head, *next;
|
||||
|
||||
if (!adev)
|
||||
return NULL;
|
||||
|
||||
head = &adev->children;
|
||||
if (list_empty(head))
|
||||
return NULL;
|
||||
|
||||
if (!child)
|
||||
return list_first_entry(head, struct acpi_device, node);
|
||||
|
||||
next = child->node.next;
|
||||
return next == head ? NULL : list_entry(next, struct acpi_device, node);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------------------
|
||||
Driver Management
|
||||
-------------------------------------------------------------------------- */
|
||||
@@ -1923,9 +2028,11 @@ void acpi_init_device_object(struct acpi_device *device, acpi_handle handle,
|
||||
device->device_type = type;
|
||||
device->handle = handle;
|
||||
device->parent = acpi_bus_get_parent(handle);
|
||||
device->fwnode.type = FWNODE_ACPI;
|
||||
acpi_set_device_status(device, sta);
|
||||
acpi_device_get_busid(device);
|
||||
acpi_set_pnp_ids(handle, &device->pnp, type);
|
||||
acpi_init_properties(device);
|
||||
acpi_bus_get_flags(device);
|
||||
device->flags.match_driver = false;
|
||||
device->flags.initialized = true;
|
||||
|
||||
@@ -4,7 +4,7 @@ obj-y := component.o core.o bus.o dd.o syscore.o \
|
||||
driver.o class.o platform.o \
|
||||
cpu.o firmware.o init.o map.o devres.o \
|
||||
attribute_container.o transport_class.o \
|
||||
topology.o container.o
|
||||
topology.o container.o property.o
|
||||
obj-$(CONFIG_DEVTMPFS) += devtmpfs.o
|
||||
obj-$(CONFIG_DMA_CMA) += dma-contiguous.o
|
||||
obj-y += power/
|
||||
|
||||
@@ -0,0 +1,431 @@
|
||||
/*
|
||||
* property.c - Unified device property interface.
|
||||
*
|
||||
* Copyright (C) 2014, Intel Corporation
|
||||
* Authors: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
|
||||
* Mika Westerberg <mika.westerberg@linux.intel.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <linux/property.h>
|
||||
#include <linux/export.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
/**
|
||||
* device_property_present - check if a property of a device is present
|
||||
* @dev: Device whose property is being checked
|
||||
* @propname: Name of the property
|
||||
*
|
||||
* Check if property @propname is present in the device firmware description.
|
||||
*/
|
||||
bool device_property_present(struct device *dev, const char *propname)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_OF) && dev->of_node)
|
||||
return of_property_read_bool(dev->of_node, propname);
|
||||
|
||||
return !acpi_dev_prop_get(ACPI_COMPANION(dev), propname, NULL);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_property_present);
|
||||
|
||||
/**
|
||||
* fwnode_property_present - check if a property of a firmware node is present
|
||||
* @fwnode: Firmware node whose property to check
|
||||
* @propname: Name of the property
|
||||
*/
|
||||
bool fwnode_property_present(struct fwnode_handle *fwnode, const char *propname)
|
||||
{
|
||||
if (is_of_node(fwnode))
|
||||
return of_property_read_bool(of_node(fwnode), propname);
|
||||
else if (is_acpi_node(fwnode))
|
||||
return !acpi_dev_prop_get(acpi_node(fwnode), propname, NULL);
|
||||
|
||||
return false;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_property_present);
|
||||
|
||||
#define OF_DEV_PROP_READ_ARRAY(node, propname, type, val, nval) \
|
||||
(val) ? of_property_read_##type##_array((node), (propname), (val), (nval)) \
|
||||
: of_property_count_elems_of_size((node), (propname), sizeof(type))
|
||||
|
||||
#define DEV_PROP_READ_ARRAY(_dev_, _propname_, _type_, _proptype_, _val_, _nval_) \
|
||||
IS_ENABLED(CONFIG_OF) && _dev_->of_node ? \
|
||||
(OF_DEV_PROP_READ_ARRAY(_dev_->of_node, _propname_, _type_, \
|
||||
_val_, _nval_)) : \
|
||||
acpi_dev_prop_read(ACPI_COMPANION(_dev_), _propname_, \
|
||||
_proptype_, _val_, _nval_)
|
||||
|
||||
/**
|
||||
* device_property_read_u8_array - return a u8 array property of a device
|
||||
* @dev: Device to get the property of
|
||||
* @propname: Name of the property
|
||||
* @val: The values are stored here
|
||||
* @nval: Size of the @val array
|
||||
*
|
||||
* Function reads an array of u8 properties with @propname from the device
|
||||
* firmware description and stores them to @val if found.
|
||||
*
|
||||
* Return: %0 if the property was found (success),
|
||||
* %-EINVAL if given arguments are not valid,
|
||||
* %-ENODATA if the property does not have a value,
|
||||
* %-EPROTO if the property is not an array of numbers,
|
||||
* %-EOVERFLOW if the size of the property is not as expected.
|
||||
*/
|
||||
int device_property_read_u8_array(struct device *dev, const char *propname,
|
||||
u8 *val, size_t nval)
|
||||
{
|
||||
return DEV_PROP_READ_ARRAY(dev, propname, u8, DEV_PROP_U8, val, nval);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_property_read_u8_array);
|
||||
|
||||
/**
|
||||
* device_property_read_u16_array - return a u16 array property of a device
|
||||
* @dev: Device to get the property of
|
||||
* @propname: Name of the property
|
||||
* @val: The values are stored here
|
||||
* @nval: Size of the @val array
|
||||
*
|
||||
* Function reads an array of u16 properties with @propname from the device
|
||||
* firmware description and stores them to @val if found.
|
||||
*
|
||||
* Return: %0 if the property was found (success),
|
||||
* %-EINVAL if given arguments are not valid,
|
||||
* %-ENODATA if the property does not have a value,
|
||||
* %-EPROTO if the property is not an array of numbers,
|
||||
* %-EOVERFLOW if the size of the property is not as expected.
|
||||
*/
|
||||
int device_property_read_u16_array(struct device *dev, const char *propname,
|
||||
u16 *val, size_t nval)
|
||||
{
|
||||
return DEV_PROP_READ_ARRAY(dev, propname, u16, DEV_PROP_U16, val, nval);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_property_read_u16_array);
|
||||
|
||||
/**
|
||||
* device_property_read_u32_array - return a u32 array property of a device
|
||||
* @dev: Device to get the property of
|
||||
* @propname: Name of the property
|
||||
* @val: The values are stored here
|
||||
* @nval: Size of the @val array
|
||||
*
|
||||
* Function reads an array of u32 properties with @propname from the device
|
||||
* firmware description and stores them to @val if found.
|
||||
*
|
||||
* Return: %0 if the property was found (success),
|
||||
* %-EINVAL if given arguments are not valid,
|
||||
* %-ENODATA if the property does not have a value,
|
||||
* %-EPROTO if the property is not an array of numbers,
|
||||
* %-EOVERFLOW if the size of the property is not as expected.
|
||||
*/
|
||||
int device_property_read_u32_array(struct device *dev, const char *propname,
|
||||
u32 *val, size_t nval)
|
||||
{
|
||||
return DEV_PROP_READ_ARRAY(dev, propname, u32, DEV_PROP_U32, val, nval);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_property_read_u32_array);
|
||||
|
||||
/**
|
||||
* device_property_read_u64_array - return a u64 array property of a device
|
||||
* @dev: Device to get the property of
|
||||
* @propname: Name of the property
|
||||
* @val: The values are stored here
|
||||
* @nval: Size of the @val array
|
||||
*
|
||||
* Function reads an array of u64 properties with @propname from the device
|
||||
* firmware description and stores them to @val if found.
|
||||
*
|
||||
* Return: %0 if the property was found (success),
|
||||
* %-EINVAL if given arguments are not valid,
|
||||
* %-ENODATA if the property does not have a value,
|
||||
* %-EPROTO if the property is not an array of numbers,
|
||||
* %-EOVERFLOW if the size of the property is not as expected.
|
||||
*/
|
||||
int device_property_read_u64_array(struct device *dev, const char *propname,
|
||||
u64 *val, size_t nval)
|
||||
{
|
||||
return DEV_PROP_READ_ARRAY(dev, propname, u64, DEV_PROP_U64, val, nval);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_property_read_u64_array);
|
||||
|
||||
/**
|
||||
* device_property_read_string_array - return a string array property of device
|
||||
* @dev: Device to get the property of
|
||||
* @propname: Name of the property
|
||||
* @val: The values are stored here
|
||||
* @nval: Size of the @val array
|
||||
*
|
||||
* Function reads an array of string properties with @propname from the device
|
||||
* firmware description and stores them to @val if found.
|
||||
*
|
||||
* Return: %0 if the property was found (success),
|
||||
* %-EINVAL if given arguments are not valid,
|
||||
* %-ENODATA if the property does not have a value,
|
||||
* %-EPROTO or %-EILSEQ if the property is not an array of strings,
|
||||
* %-EOVERFLOW if the size of the property is not as expected.
|
||||
*/
|
||||
int device_property_read_string_array(struct device *dev, const char *propname,
|
||||
const char **val, size_t nval)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_OF) && dev->of_node ?
|
||||
of_property_read_string_array(dev->of_node, propname, val, nval) :
|
||||
acpi_dev_prop_read(ACPI_COMPANION(dev), propname,
|
||||
DEV_PROP_STRING, val, nval);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_property_read_string_array);
|
||||
|
||||
/**
|
||||
* device_property_read_string - return a string property of a device
|
||||
* @dev: Device to get the property of
|
||||
* @propname: Name of the property
|
||||
* @val: The value is stored here
|
||||
*
|
||||
* Function reads property @propname from the device firmware description and
|
||||
* stores the value into @val if found. The value is checked to be a string.
|
||||
*
|
||||
* Return: %0 if the property was found (success),
|
||||
* %-EINVAL if given arguments are not valid,
|
||||
* %-ENODATA if the property does not have a value,
|
||||
* %-EPROTO or %-EILSEQ if the property type is not a string.
|
||||
*/
|
||||
int device_property_read_string(struct device *dev, const char *propname,
|
||||
const char **val)
|
||||
{
|
||||
return IS_ENABLED(CONFIG_OF) && dev->of_node ?
|
||||
of_property_read_string(dev->of_node, propname, val) :
|
||||
acpi_dev_prop_read(ACPI_COMPANION(dev), propname,
|
||||
DEV_PROP_STRING, val, 1);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_property_read_string);
|
||||
|
||||
#define FWNODE_PROP_READ_ARRAY(_fwnode_, _propname_, _type_, _proptype_, _val_, _nval_) \
|
||||
({ \
|
||||
int _ret_; \
|
||||
if (is_of_node(_fwnode_)) \
|
||||
_ret_ = OF_DEV_PROP_READ_ARRAY(of_node(_fwnode_), _propname_, \
|
||||
_type_, _val_, _nval_); \
|
||||
else if (is_acpi_node(_fwnode_)) \
|
||||
_ret_ = acpi_dev_prop_read(acpi_node(_fwnode_), _propname_, \
|
||||
_proptype_, _val_, _nval_); \
|
||||
else \
|
||||
_ret_ = -ENXIO; \
|
||||
_ret_; \
|
||||
})
|
||||
|
||||
/**
|
||||
* fwnode_property_read_u8_array - return a u8 array property of firmware node
|
||||
* @fwnode: Firmware node to get the property of
|
||||
* @propname: Name of the property
|
||||
* @val: The values are stored here
|
||||
* @nval: Size of the @val array
|
||||
*
|
||||
* Read an array of u8 properties with @propname from @fwnode and stores them to
|
||||
* @val if found.
|
||||
*
|
||||
* Return: %0 if the property was found (success),
|
||||
* %-EINVAL if given arguments are not valid,
|
||||
* %-ENODATA if the property does not have a value,
|
||||
* %-EPROTO if the property is not an array of numbers,
|
||||
* %-EOVERFLOW if the size of the property is not as expected,
|
||||
* %-ENXIO if no suitable firmware interface is present.
|
||||
*/
|
||||
int fwnode_property_read_u8_array(struct fwnode_handle *fwnode,
|
||||
const char *propname, u8 *val, size_t nval)
|
||||
{
|
||||
return FWNODE_PROP_READ_ARRAY(fwnode, propname, u8, DEV_PROP_U8,
|
||||
val, nval);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_property_read_u8_array);
|
||||
|
||||
/**
|
||||
* fwnode_property_read_u16_array - return a u16 array property of firmware node
|
||||
* @fwnode: Firmware node to get the property of
|
||||
* @propname: Name of the property
|
||||
* @val: The values are stored here
|
||||
* @nval: Size of the @val array
|
||||
*
|
||||
* Read an array of u16 properties with @propname from @fwnode and store them to
|
||||
* @val if found.
|
||||
*
|
||||
* Return: %0 if the property was found (success),
|
||||
* %-EINVAL if given arguments are not valid,
|
||||
* %-ENODATA if the property does not have a value,
|
||||
* %-EPROTO if the property is not an array of numbers,
|
||||
* %-EOVERFLOW if the size of the property is not as expected,
|
||||
* %-ENXIO if no suitable firmware interface is present.
|
||||
*/
|
||||
int fwnode_property_read_u16_array(struct fwnode_handle *fwnode,
|
||||
const char *propname, u16 *val, size_t nval)
|
||||
{
|
||||
return FWNODE_PROP_READ_ARRAY(fwnode, propname, u16, DEV_PROP_U16,
|
||||
val, nval);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_property_read_u16_array);
|
||||
|
||||
/**
|
||||
* fwnode_property_read_u32_array - return a u32 array property of firmware node
|
||||
* @fwnode: Firmware node to get the property of
|
||||
* @propname: Name of the property
|
||||
* @val: The values are stored here
|
||||
* @nval: Size of the @val array
|
||||
*
|
||||
* Read an array of u32 properties with @propname from @fwnode store them to
|
||||
* @val if found.
|
||||
*
|
||||
* Return: %0 if the property was found (success),
|
||||
* %-EINVAL if given arguments are not valid,
|
||||
* %-ENODATA if the property does not have a value,
|
||||
* %-EPROTO if the property is not an array of numbers,
|
||||
* %-EOVERFLOW if the size of the property is not as expected,
|
||||
* %-ENXIO if no suitable firmware interface is present.
|
||||
*/
|
||||
int fwnode_property_read_u32_array(struct fwnode_handle *fwnode,
|
||||
const char *propname, u32 *val, size_t nval)
|
||||
{
|
||||
return FWNODE_PROP_READ_ARRAY(fwnode, propname, u32, DEV_PROP_U32,
|
||||
val, nval);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_property_read_u32_array);
|
||||
|
||||
/**
|
||||
* fwnode_property_read_u64_array - return a u64 array property firmware node
|
||||
* @fwnode: Firmware node to get the property of
|
||||
* @propname: Name of the property
|
||||
* @val: The values are stored here
|
||||
* @nval: Size of the @val array
|
||||
*
|
||||
* Read an array of u64 properties with @propname from @fwnode and store them to
|
||||
* @val if found.
|
||||
*
|
||||
* Return: %0 if the property was found (success),
|
||||
* %-EINVAL if given arguments are not valid,
|
||||
* %-ENODATA if the property does not have a value,
|
||||
* %-EPROTO if the property is not an array of numbers,
|
||||
* %-EOVERFLOW if the size of the property is not as expected,
|
||||
* %-ENXIO if no suitable firmware interface is present.
|
||||
*/
|
||||
int fwnode_property_read_u64_array(struct fwnode_handle *fwnode,
|
||||
const char *propname, u64 *val, size_t nval)
|
||||
{
|
||||
return FWNODE_PROP_READ_ARRAY(fwnode, propname, u64, DEV_PROP_U64,
|
||||
val, nval);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_property_read_u64_array);
|
||||
|
||||
/**
|
||||
* fwnode_property_read_string_array - return string array property of a node
|
||||
* @fwnode: Firmware node to get the property of
|
||||
* @propname: Name of the property
|
||||
* @val: The values are stored here
|
||||
* @nval: Size of the @val array
|
||||
*
|
||||
* Read an string list property @propname from the given firmware node and store
|
||||
* them to @val if found.
|
||||
*
|
||||
* Return: %0 if the property was found (success),
|
||||
* %-EINVAL if given arguments are not valid,
|
||||
* %-ENODATA if the property does not have a value,
|
||||
* %-EPROTO if the property is not an array of strings,
|
||||
* %-EOVERFLOW if the size of the property is not as expected,
|
||||
* %-ENXIO if no suitable firmware interface is present.
|
||||
*/
|
||||
int fwnode_property_read_string_array(struct fwnode_handle *fwnode,
|
||||
const char *propname, const char **val,
|
||||
size_t nval)
|
||||
{
|
||||
if (is_of_node(fwnode))
|
||||
return of_property_read_string_array(of_node(fwnode), propname,
|
||||
val, nval);
|
||||
else if (is_acpi_node(fwnode))
|
||||
return acpi_dev_prop_read(acpi_node(fwnode), propname,
|
||||
DEV_PROP_STRING, val, nval);
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_property_read_string_array);
|
||||
|
||||
/**
|
||||
* fwnode_property_read_string - return a string property of a firmware node
|
||||
* @fwnode: Firmware node to get the property of
|
||||
* @propname: Name of the property
|
||||
* @val: The value is stored here
|
||||
*
|
||||
* Read property @propname from the given firmware node and store the value into
|
||||
* @val if found. The value is checked to be a string.
|
||||
*
|
||||
* Return: %0 if the property was found (success),
|
||||
* %-EINVAL if given arguments are not valid,
|
||||
* %-ENODATA if the property does not have a value,
|
||||
* %-EPROTO or %-EILSEQ if the property is not a string,
|
||||
* %-ENXIO if no suitable firmware interface is present.
|
||||
*/
|
||||
int fwnode_property_read_string(struct fwnode_handle *fwnode,
|
||||
const char *propname, const char **val)
|
||||
{
|
||||
if (is_of_node(fwnode))
|
||||
return of_property_read_string(of_node(fwnode),propname, val);
|
||||
else if (is_acpi_node(fwnode))
|
||||
return acpi_dev_prop_read(acpi_node(fwnode), propname,
|
||||
DEV_PROP_STRING, val, 1);
|
||||
|
||||
return -ENXIO;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_property_read_string);
|
||||
|
||||
/**
|
||||
* device_get_next_child_node - Return the next child node handle for a device
|
||||
* @dev: Device to find the next child node for.
|
||||
* @child: Handle to one of the device's child nodes or a null handle.
|
||||
*/
|
||||
struct fwnode_handle *device_get_next_child_node(struct device *dev,
|
||||
struct fwnode_handle *child)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_OF) && dev->of_node) {
|
||||
struct device_node *node;
|
||||
|
||||
node = of_get_next_available_child(dev->of_node, of_node(child));
|
||||
if (node)
|
||||
return &node->fwnode;
|
||||
} else if (IS_ENABLED(CONFIG_ACPI)) {
|
||||
struct acpi_device *node;
|
||||
|
||||
node = acpi_get_next_child(dev, acpi_node(child));
|
||||
if (node)
|
||||
return acpi_fwnode_handle(node);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_get_next_child_node);
|
||||
|
||||
/**
|
||||
* fwnode_handle_put - Drop reference to a device node
|
||||
* @fwnode: Pointer to the device node to drop the reference to.
|
||||
*
|
||||
* This has to be used when terminating device_for_each_child_node() iteration
|
||||
* with break or return to prevent stale device node references from being left
|
||||
* behind.
|
||||
*/
|
||||
void fwnode_handle_put(struct fwnode_handle *fwnode)
|
||||
{
|
||||
if (is_of_node(fwnode))
|
||||
of_node_put(of_node(fwnode));
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_handle_put);
|
||||
|
||||
/**
|
||||
* device_get_child_node_count - return the number of child nodes for device
|
||||
* @dev: Device to cound the child nodes for
|
||||
*/
|
||||
unsigned int device_get_child_node_count(struct device *dev)
|
||||
{
|
||||
struct fwnode_handle *child;
|
||||
unsigned int count = 0;
|
||||
|
||||
device_for_each_child_node(dev, child)
|
||||
count++;
|
||||
|
||||
return count;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_get_child_node_count);
|
||||
@@ -108,6 +108,38 @@ struct gpio_desc *__must_check __devm_gpiod_get_index(struct device *dev,
|
||||
}
|
||||
EXPORT_SYMBOL(__devm_gpiod_get_index);
|
||||
|
||||
/**
|
||||
* devm_get_gpiod_from_child - get a GPIO descriptor from a device's child node
|
||||
* @dev: GPIO consumer
|
||||
* @child: firmware node (child of @dev)
|
||||
*
|
||||
* GPIO descriptors returned from this function are automatically disposed on
|
||||
* driver detach.
|
||||
*/
|
||||
struct gpio_desc *devm_get_gpiod_from_child(struct device *dev,
|
||||
struct fwnode_handle *child)
|
||||
{
|
||||
struct gpio_desc **dr;
|
||||
struct gpio_desc *desc;
|
||||
|
||||
dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *),
|
||||
GFP_KERNEL);
|
||||
if (!dr)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
desc = fwnode_get_named_gpiod(child, "gpios");
|
||||
if (IS_ERR(desc)) {
|
||||
devres_free(dr);
|
||||
return desc;
|
||||
}
|
||||
|
||||
*dr = desc;
|
||||
devres_add(dev, dr);
|
||||
|
||||
return desc;
|
||||
}
|
||||
EXPORT_SYMBOL(devm_get_gpiod_from_child);
|
||||
|
||||
/**
|
||||
* devm_gpiod_get_index_optional - Resource-managed gpiod_get_index_optional()
|
||||
* @dev: GPIO consumer
|
||||
|
||||
+118
-187
@@ -29,84 +29,129 @@
|
||||
|
||||
#include <linux/gpio.h>
|
||||
|
||||
static DEFINE_SPINLOCK(gpio_lock);
|
||||
#define GEN 0x00
|
||||
#define GIO 0x04
|
||||
#define GLV 0x08
|
||||
|
||||
#define CGEN (0x00)
|
||||
#define CGIO (0x04)
|
||||
#define CGLV (0x08)
|
||||
struct sch_gpio {
|
||||
struct gpio_chip chip;
|
||||
spinlock_t lock;
|
||||
unsigned short iobase;
|
||||
unsigned short core_base;
|
||||
unsigned short resume_base;
|
||||
};
|
||||
|
||||
#define RGEN (0x20)
|
||||
#define RGIO (0x24)
|
||||
#define RGLV (0x28)
|
||||
#define to_sch_gpio(c) container_of(c, struct sch_gpio, chip)
|
||||
|
||||
static unsigned short gpio_ba;
|
||||
|
||||
static int sch_gpio_core_direction_in(struct gpio_chip *gc, unsigned gpio_num)
|
||||
static unsigned sch_gpio_offset(struct sch_gpio *sch, unsigned gpio,
|
||||
unsigned reg)
|
||||
{
|
||||
unsigned base = 0;
|
||||
|
||||
if (gpio >= sch->resume_base) {
|
||||
gpio -= sch->resume_base;
|
||||
base += 0x20;
|
||||
}
|
||||
|
||||
return base + reg + gpio / 8;
|
||||
}
|
||||
|
||||
static unsigned sch_gpio_bit(struct sch_gpio *sch, unsigned gpio)
|
||||
{
|
||||
if (gpio >= sch->resume_base)
|
||||
gpio -= sch->resume_base;
|
||||
return gpio % 8;
|
||||
}
|
||||
|
||||
static void sch_gpio_enable(struct sch_gpio *sch, unsigned gpio)
|
||||
{
|
||||
unsigned short offset, bit;
|
||||
u8 enable;
|
||||
|
||||
spin_lock(&sch->lock);
|
||||
|
||||
offset = sch_gpio_offset(sch, gpio, GEN);
|
||||
bit = sch_gpio_bit(sch, gpio);
|
||||
|
||||
enable = inb(sch->iobase + offset);
|
||||
if (!(enable & (1 << bit)))
|
||||
outb(enable | (1 << bit), sch->iobase + offset);
|
||||
|
||||
spin_unlock(&sch->lock);
|
||||
}
|
||||
|
||||
static int sch_gpio_direction_in(struct gpio_chip *gc, unsigned gpio_num)
|
||||
{
|
||||
struct sch_gpio *sch = to_sch_gpio(gc);
|
||||
u8 curr_dirs;
|
||||
unsigned short offset, bit;
|
||||
|
||||
spin_lock(&gpio_lock);
|
||||
spin_lock(&sch->lock);
|
||||
|
||||
offset = CGIO + gpio_num / 8;
|
||||
bit = gpio_num % 8;
|
||||
offset = sch_gpio_offset(sch, gpio_num, GIO);
|
||||
bit = sch_gpio_bit(sch, gpio_num);
|
||||
|
||||
curr_dirs = inb(gpio_ba + offset);
|
||||
curr_dirs = inb(sch->iobase + offset);
|
||||
|
||||
if (!(curr_dirs & (1 << bit)))
|
||||
outb(curr_dirs | (1 << bit), gpio_ba + offset);
|
||||
outb(curr_dirs | (1 << bit), sch->iobase + offset);
|
||||
|
||||
spin_unlock(&gpio_lock);
|
||||
spin_unlock(&sch->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sch_gpio_core_get(struct gpio_chip *gc, unsigned gpio_num)
|
||||
static int sch_gpio_get(struct gpio_chip *gc, unsigned gpio_num)
|
||||
{
|
||||
struct sch_gpio *sch = to_sch_gpio(gc);
|
||||
int res;
|
||||
unsigned short offset, bit;
|
||||
|
||||
offset = CGLV + gpio_num / 8;
|
||||
bit = gpio_num % 8;
|
||||
offset = sch_gpio_offset(sch, gpio_num, GLV);
|
||||
bit = sch_gpio_bit(sch, gpio_num);
|
||||
|
||||
res = !!(inb(sch->iobase + offset) & (1 << bit));
|
||||
|
||||
res = !!(inb(gpio_ba + offset) & (1 << bit));
|
||||
return res;
|
||||
}
|
||||
|
||||
static void sch_gpio_core_set(struct gpio_chip *gc, unsigned gpio_num, int val)
|
||||
static void sch_gpio_set(struct gpio_chip *gc, unsigned gpio_num, int val)
|
||||
{
|
||||
struct sch_gpio *sch = to_sch_gpio(gc);
|
||||
u8 curr_vals;
|
||||
unsigned short offset, bit;
|
||||
|
||||
spin_lock(&gpio_lock);
|
||||
spin_lock(&sch->lock);
|
||||
|
||||
offset = CGLV + gpio_num / 8;
|
||||
bit = gpio_num % 8;
|
||||
offset = sch_gpio_offset(sch, gpio_num, GLV);
|
||||
bit = sch_gpio_bit(sch, gpio_num);
|
||||
|
||||
curr_vals = inb(gpio_ba + offset);
|
||||
curr_vals = inb(sch->iobase + offset);
|
||||
|
||||
if (val)
|
||||
outb(curr_vals | (1 << bit), gpio_ba + offset);
|
||||
outb(curr_vals | (1 << bit), sch->iobase + offset);
|
||||
else
|
||||
outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
|
||||
spin_unlock(&gpio_lock);
|
||||
outb((curr_vals & ~(1 << bit)), sch->iobase + offset);
|
||||
|
||||
spin_unlock(&sch->lock);
|
||||
}
|
||||
|
||||
static int sch_gpio_core_direction_out(struct gpio_chip *gc,
|
||||
unsigned gpio_num, int val)
|
||||
static int sch_gpio_direction_out(struct gpio_chip *gc, unsigned gpio_num,
|
||||
int val)
|
||||
{
|
||||
struct sch_gpio *sch = to_sch_gpio(gc);
|
||||
u8 curr_dirs;
|
||||
unsigned short offset, bit;
|
||||
|
||||
spin_lock(&gpio_lock);
|
||||
spin_lock(&sch->lock);
|
||||
|
||||
offset = CGIO + gpio_num / 8;
|
||||
bit = gpio_num % 8;
|
||||
offset = sch_gpio_offset(sch, gpio_num, GIO);
|
||||
bit = sch_gpio_bit(sch, gpio_num);
|
||||
|
||||
curr_dirs = inb(gpio_ba + offset);
|
||||
curr_dirs = inb(sch->iobase + offset);
|
||||
if (curr_dirs & (1 << bit))
|
||||
outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
|
||||
outb(curr_dirs & ~(1 << bit), sch->iobase + offset);
|
||||
|
||||
spin_unlock(&gpio_lock);
|
||||
spin_unlock(&sch->lock);
|
||||
|
||||
/*
|
||||
* according to the datasheet, writing to the level register has no
|
||||
@@ -117,202 +162,88 @@ static int sch_gpio_core_direction_out(struct gpio_chip *gc,
|
||||
* But we cannot prevent a short low pulse if direction is set to high
|
||||
* and an external pull-up is connected.
|
||||
*/
|
||||
sch_gpio_core_set(gc, gpio_num, val);
|
||||
sch_gpio_set(gc, gpio_num, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gpio_chip sch_gpio_core = {
|
||||
.label = "sch_gpio_core",
|
||||
static struct gpio_chip sch_gpio_chip = {
|
||||
.label = "sch_gpio",
|
||||
.owner = THIS_MODULE,
|
||||
.direction_input = sch_gpio_core_direction_in,
|
||||
.get = sch_gpio_core_get,
|
||||
.direction_output = sch_gpio_core_direction_out,
|
||||
.set = sch_gpio_core_set,
|
||||
};
|
||||
|
||||
static int sch_gpio_resume_direction_in(struct gpio_chip *gc,
|
||||
unsigned gpio_num)
|
||||
{
|
||||
u8 curr_dirs;
|
||||
unsigned short offset, bit;
|
||||
|
||||
spin_lock(&gpio_lock);
|
||||
|
||||
offset = RGIO + gpio_num / 8;
|
||||
bit = gpio_num % 8;
|
||||
|
||||
curr_dirs = inb(gpio_ba + offset);
|
||||
|
||||
if (!(curr_dirs & (1 << bit)))
|
||||
outb(curr_dirs | (1 << bit), gpio_ba + offset);
|
||||
|
||||
spin_unlock(&gpio_lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sch_gpio_resume_get(struct gpio_chip *gc, unsigned gpio_num)
|
||||
{
|
||||
unsigned short offset, bit;
|
||||
|
||||
offset = RGLV + gpio_num / 8;
|
||||
bit = gpio_num % 8;
|
||||
|
||||
return !!(inb(gpio_ba + offset) & (1 << bit));
|
||||
}
|
||||
|
||||
static void sch_gpio_resume_set(struct gpio_chip *gc,
|
||||
unsigned gpio_num, int val)
|
||||
{
|
||||
u8 curr_vals;
|
||||
unsigned short offset, bit;
|
||||
|
||||
spin_lock(&gpio_lock);
|
||||
|
||||
offset = RGLV + gpio_num / 8;
|
||||
bit = gpio_num % 8;
|
||||
|
||||
curr_vals = inb(gpio_ba + offset);
|
||||
|
||||
if (val)
|
||||
outb(curr_vals | (1 << bit), gpio_ba + offset);
|
||||
else
|
||||
outb((curr_vals & ~(1 << bit)), gpio_ba + offset);
|
||||
|
||||
spin_unlock(&gpio_lock);
|
||||
}
|
||||
|
||||
static int sch_gpio_resume_direction_out(struct gpio_chip *gc,
|
||||
unsigned gpio_num, int val)
|
||||
{
|
||||
u8 curr_dirs;
|
||||
unsigned short offset, bit;
|
||||
|
||||
offset = RGIO + gpio_num / 8;
|
||||
bit = gpio_num % 8;
|
||||
|
||||
spin_lock(&gpio_lock);
|
||||
|
||||
curr_dirs = inb(gpio_ba + offset);
|
||||
if (curr_dirs & (1 << bit))
|
||||
outb(curr_dirs & ~(1 << bit), gpio_ba + offset);
|
||||
|
||||
spin_unlock(&gpio_lock);
|
||||
|
||||
/*
|
||||
* according to the datasheet, writing to the level register has no
|
||||
* effect when GPIO is programmed as input.
|
||||
* Actually the the level register is read-only when configured as input.
|
||||
* Thus presetting the output level before switching to output is _NOT_ possible.
|
||||
* Hence we set the level after configuring the GPIO as output.
|
||||
* But we cannot prevent a short low pulse if direction is set to high
|
||||
* and an external pull-up is connected.
|
||||
*/
|
||||
sch_gpio_resume_set(gc, gpio_num, val);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gpio_chip sch_gpio_resume = {
|
||||
.label = "sch_gpio_resume",
|
||||
.owner = THIS_MODULE,
|
||||
.direction_input = sch_gpio_resume_direction_in,
|
||||
.get = sch_gpio_resume_get,
|
||||
.direction_output = sch_gpio_resume_direction_out,
|
||||
.set = sch_gpio_resume_set,
|
||||
.direction_input = sch_gpio_direction_in,
|
||||
.get = sch_gpio_get,
|
||||
.direction_output = sch_gpio_direction_out,
|
||||
.set = sch_gpio_set,
|
||||
};
|
||||
|
||||
static int sch_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sch_gpio *sch;
|
||||
struct resource *res;
|
||||
int err, id;
|
||||
|
||||
id = pdev->id;
|
||||
if (!id)
|
||||
return -ENODEV;
|
||||
sch = devm_kzalloc(&pdev->dev, sizeof(*sch), GFP_KERNEL);
|
||||
if (!sch)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
if (!res)
|
||||
return -EBUSY;
|
||||
|
||||
if (!request_region(res->start, resource_size(res), pdev->name))
|
||||
if (!devm_request_region(&pdev->dev, res->start, resource_size(res),
|
||||
pdev->name))
|
||||
return -EBUSY;
|
||||
|
||||
gpio_ba = res->start;
|
||||
spin_lock_init(&sch->lock);
|
||||
sch->iobase = res->start;
|
||||
sch->chip = sch_gpio_chip;
|
||||
sch->chip.label = dev_name(&pdev->dev);
|
||||
sch->chip.dev = &pdev->dev;
|
||||
|
||||
switch (id) {
|
||||
switch (pdev->id) {
|
||||
case PCI_DEVICE_ID_INTEL_SCH_LPC:
|
||||
sch_gpio_core.base = 0;
|
||||
sch_gpio_core.ngpio = 10;
|
||||
sch_gpio_resume.base = 10;
|
||||
sch_gpio_resume.ngpio = 4;
|
||||
sch->core_base = 0;
|
||||
sch->resume_base = 10;
|
||||
sch->chip.ngpio = 14;
|
||||
|
||||
/*
|
||||
* GPIO[6:0] enabled by default
|
||||
* GPIO7 is configured by the CMC as SLPIOVR
|
||||
* Enable GPIO[9:8] core powered gpios explicitly
|
||||
*/
|
||||
outb(0x3, gpio_ba + CGEN + 1);
|
||||
sch_gpio_enable(sch, 8);
|
||||
sch_gpio_enable(sch, 9);
|
||||
/*
|
||||
* SUS_GPIO[2:0] enabled by default
|
||||
* Enable SUS_GPIO3 resume powered gpio explicitly
|
||||
*/
|
||||
outb(0x8, gpio_ba + RGEN);
|
||||
sch_gpio_enable(sch, 13);
|
||||
break;
|
||||
|
||||
case PCI_DEVICE_ID_INTEL_ITC_LPC:
|
||||
sch_gpio_core.base = 0;
|
||||
sch_gpio_core.ngpio = 5;
|
||||
sch_gpio_resume.base = 5;
|
||||
sch_gpio_resume.ngpio = 9;
|
||||
sch->core_base = 0;
|
||||
sch->resume_base = 5;
|
||||
sch->chip.ngpio = 14;
|
||||
break;
|
||||
|
||||
case PCI_DEVICE_ID_INTEL_CENTERTON_ILB:
|
||||
sch_gpio_core.base = 0;
|
||||
sch_gpio_core.ngpio = 21;
|
||||
sch_gpio_resume.base = 21;
|
||||
sch_gpio_resume.ngpio = 9;
|
||||
sch->core_base = 0;
|
||||
sch->resume_base = 21;
|
||||
sch->chip.ngpio = 30;
|
||||
break;
|
||||
|
||||
default:
|
||||
err = -ENODEV;
|
||||
goto err_sch_gpio_core;
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sch_gpio_core.dev = &pdev->dev;
|
||||
sch_gpio_resume.dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, sch);
|
||||
|
||||
err = gpiochip_add(&sch_gpio_core);
|
||||
if (err < 0)
|
||||
goto err_sch_gpio_core;
|
||||
|
||||
err = gpiochip_add(&sch_gpio_resume);
|
||||
if (err < 0)
|
||||
goto err_sch_gpio_resume;
|
||||
|
||||
return 0;
|
||||
|
||||
err_sch_gpio_resume:
|
||||
gpiochip_remove(&sch_gpio_core);
|
||||
|
||||
err_sch_gpio_core:
|
||||
release_region(res->start, resource_size(res));
|
||||
gpio_ba = 0;
|
||||
|
||||
return err;
|
||||
return gpiochip_add(&sch->chip);
|
||||
}
|
||||
|
||||
static int sch_gpio_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
if (gpio_ba) {
|
||||
|
||||
gpiochip_remove(&sch_gpio_core);
|
||||
gpiochip_remove(&sch_gpio_resume);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
|
||||
release_region(res->start, resource_size(res));
|
||||
gpio_ba = 0;
|
||||
}
|
||||
struct sch_gpio *sch = platform_get_drvdata(pdev);
|
||||
|
||||
gpiochip_remove(&sch->chip);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
+103
-14
@@ -287,9 +287,45 @@ void acpi_gpiochip_free_interrupts(struct gpio_chip *chip)
|
||||
}
|
||||
}
|
||||
|
||||
int acpi_dev_add_driver_gpios(struct acpi_device *adev,
|
||||
const struct acpi_gpio_mapping *gpios)
|
||||
{
|
||||
if (adev && gpios) {
|
||||
adev->driver_gpios = gpios;
|
||||
return 0;
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(acpi_dev_add_driver_gpios);
|
||||
|
||||
static bool acpi_get_driver_gpio_data(struct acpi_device *adev,
|
||||
const char *name, int index,
|
||||
struct acpi_reference_args *args)
|
||||
{
|
||||
const struct acpi_gpio_mapping *gm;
|
||||
|
||||
if (!adev->driver_gpios)
|
||||
return false;
|
||||
|
||||
for (gm = adev->driver_gpios; gm->name; gm++)
|
||||
if (!strcmp(name, gm->name) && gm->data && index < gm->size) {
|
||||
const struct acpi_gpio_params *par = gm->data + index;
|
||||
|
||||
args->adev = adev;
|
||||
args->args[0] = par->crs_entry_index;
|
||||
args->args[1] = par->line_index;
|
||||
args->args[2] = par->active_low;
|
||||
args->nargs = 3;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct acpi_gpio_lookup {
|
||||
struct acpi_gpio_info info;
|
||||
int index;
|
||||
int pin_index;
|
||||
struct gpio_desc *desc;
|
||||
int n;
|
||||
};
|
||||
@@ -303,13 +339,24 @@ static int acpi_find_gpio(struct acpi_resource *ares, void *data)
|
||||
|
||||
if (lookup->n++ == lookup->index && !lookup->desc) {
|
||||
const struct acpi_resource_gpio *agpio = &ares->data.gpio;
|
||||
int pin_index = lookup->pin_index;
|
||||
|
||||
if (pin_index >= agpio->pin_table_length)
|
||||
return 1;
|
||||
|
||||
lookup->desc = acpi_get_gpiod(agpio->resource_source.string_ptr,
|
||||
agpio->pin_table[0]);
|
||||
agpio->pin_table[pin_index]);
|
||||
lookup->info.gpioint =
|
||||
agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT;
|
||||
lookup->info.active_low =
|
||||
agpio->polarity == ACPI_ACTIVE_LOW;
|
||||
|
||||
/*
|
||||
* ActiveLow is only specified for GpioInt resource. If
|
||||
* GpioIo is used then the only way to set the flag is
|
||||
* to use _DSD "gpios" property.
|
||||
*/
|
||||
if (lookup->info.gpioint)
|
||||
lookup->info.active_low =
|
||||
agpio->polarity == ACPI_ACTIVE_LOW;
|
||||
}
|
||||
|
||||
return 1;
|
||||
@@ -317,40 +364,79 @@ static int acpi_find_gpio(struct acpi_resource *ares, void *data)
|
||||
|
||||
/**
|
||||
* acpi_get_gpiod_by_index() - get a GPIO descriptor from device resources
|
||||
* @dev: pointer to a device to get GPIO from
|
||||
* @adev: pointer to a ACPI device to get GPIO from
|
||||
* @propname: Property name of the GPIO (optional)
|
||||
* @index: index of GpioIo/GpioInt resource (starting from %0)
|
||||
* @info: info pointer to fill in (optional)
|
||||
*
|
||||
* Function goes through ACPI resources for @dev and based on @index looks
|
||||
* Function goes through ACPI resources for @adev and based on @index looks
|
||||
* up a GpioIo/GpioInt resource, translates it to the Linux GPIO descriptor,
|
||||
* and returns it. @index matches GpioIo/GpioInt resources only so if there
|
||||
* are total %3 GPIO resources, the index goes from %0 to %2.
|
||||
*
|
||||
* If @propname is specified the GPIO is looked using device property. In
|
||||
* that case @index is used to select the GPIO entry in the property value
|
||||
* (in case of multiple).
|
||||
*
|
||||
* If the GPIO cannot be translated or there is an error an ERR_PTR is
|
||||
* returned.
|
||||
*
|
||||
* Note: if the GPIO resource has multiple entries in the pin list, this
|
||||
* function only returns the first.
|
||||
*/
|
||||
struct gpio_desc *acpi_get_gpiod_by_index(struct device *dev, int index,
|
||||
struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
|
||||
const char *propname, int index,
|
||||
struct acpi_gpio_info *info)
|
||||
{
|
||||
struct acpi_gpio_lookup lookup;
|
||||
struct list_head resource_list;
|
||||
struct acpi_device *adev;
|
||||
acpi_handle handle;
|
||||
bool active_low = false;
|
||||
int ret;
|
||||
|
||||
if (!dev)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
handle = ACPI_HANDLE(dev);
|
||||
if (!handle || acpi_bus_get_device(handle, &adev))
|
||||
if (!adev)
|
||||
return ERR_PTR(-ENODEV);
|
||||
|
||||
memset(&lookup, 0, sizeof(lookup));
|
||||
lookup.index = index;
|
||||
|
||||
if (propname) {
|
||||
struct acpi_reference_args args;
|
||||
|
||||
dev_dbg(&adev->dev, "GPIO: looking up %s\n", propname);
|
||||
|
||||
memset(&args, 0, sizeof(args));
|
||||
ret = acpi_dev_get_property_reference(adev, propname,
|
||||
index, &args);
|
||||
if (ret) {
|
||||
bool found = acpi_get_driver_gpio_data(adev, propname,
|
||||
index, &args);
|
||||
if (!found)
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
/*
|
||||
* The property was found and resolved so need to
|
||||
* lookup the GPIO based on returned args instead.
|
||||
*/
|
||||
adev = args.adev;
|
||||
if (args.nargs >= 2) {
|
||||
lookup.index = args.args[0];
|
||||
lookup.pin_index = args.args[1];
|
||||
/*
|
||||
* 3rd argument, if present is used to
|
||||
* specify active_low.
|
||||
*/
|
||||
if (args.nargs >= 3)
|
||||
active_low = !!args.args[2];
|
||||
}
|
||||
|
||||
dev_dbg(&adev->dev, "GPIO: _DSD returned %s %zd %llu %llu %llu\n",
|
||||
dev_name(&adev->dev), args.nargs,
|
||||
args.args[0], args.args[1], args.args[2]);
|
||||
} else {
|
||||
dev_dbg(&adev->dev, "GPIO: looking up %d in _CRS\n", index);
|
||||
}
|
||||
|
||||
INIT_LIST_HEAD(&resource_list);
|
||||
ret = acpi_dev_get_resources(adev, &resource_list, acpi_find_gpio,
|
||||
&lookup);
|
||||
@@ -359,8 +445,11 @@ struct gpio_desc *acpi_get_gpiod_by_index(struct device *dev, int index,
|
||||
|
||||
acpi_dev_free_resource_list(&resource_list);
|
||||
|
||||
if (lookup.desc && info)
|
||||
if (lookup.desc && info) {
|
||||
*info = lookup.info;
|
||||
if (active_low)
|
||||
info->active_low = active_low;
|
||||
}
|
||||
|
||||
return lookup.desc ? lookup.desc : ERR_PTR(-ENOENT);
|
||||
}
|
||||
|
||||
+81
-4
@@ -1505,14 +1505,36 @@ static struct gpio_desc *acpi_find_gpio(struct device *dev, const char *con_id,
|
||||
unsigned int idx,
|
||||
enum gpio_lookup_flags *flags)
|
||||
{
|
||||
static const char * const suffixes[] = { "gpios", "gpio" };
|
||||
struct acpi_device *adev = ACPI_COMPANION(dev);
|
||||
struct acpi_gpio_info info;
|
||||
struct gpio_desc *desc;
|
||||
char propname[32];
|
||||
int i;
|
||||
|
||||
desc = acpi_get_gpiod_by_index(dev, idx, &info);
|
||||
if (IS_ERR(desc))
|
||||
return desc;
|
||||
/* Try first from _DSD */
|
||||
for (i = 0; i < ARRAY_SIZE(suffixes); i++) {
|
||||
if (con_id && strcmp(con_id, "gpios")) {
|
||||
snprintf(propname, sizeof(propname), "%s-%s",
|
||||
con_id, suffixes[i]);
|
||||
} else {
|
||||
snprintf(propname, sizeof(propname), "%s",
|
||||
suffixes[i]);
|
||||
}
|
||||
|
||||
if (info.gpioint && info.active_low)
|
||||
desc = acpi_get_gpiod_by_index(adev, propname, idx, &info);
|
||||
if (!IS_ERR(desc) || (PTR_ERR(desc) == -EPROBE_DEFER))
|
||||
break;
|
||||
}
|
||||
|
||||
/* Then from plain _CRS GPIOs */
|
||||
if (IS_ERR(desc)) {
|
||||
desc = acpi_get_gpiod_by_index(adev, NULL, idx, &info);
|
||||
if (IS_ERR(desc))
|
||||
return desc;
|
||||
}
|
||||
|
||||
if (info.active_low)
|
||||
*flags |= GPIO_ACTIVE_LOW;
|
||||
|
||||
return desc;
|
||||
@@ -1712,6 +1734,61 @@ struct gpio_desc *__must_check __gpiod_get_index(struct device *dev,
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(__gpiod_get_index);
|
||||
|
||||
/**
|
||||
* fwnode_get_named_gpiod - obtain a GPIO from firmware node
|
||||
* @fwnode: handle of the firmware node
|
||||
* @propname: name of the firmware property representing the GPIO
|
||||
*
|
||||
* This function can be used for drivers that get their configuration
|
||||
* from firmware.
|
||||
*
|
||||
* Function properly finds the corresponding GPIO using whatever is the
|
||||
* underlying firmware interface and then makes sure that the GPIO
|
||||
* descriptor is requested before it is returned to the caller.
|
||||
*
|
||||
* In case of error an ERR_PTR() is returned.
|
||||
*/
|
||||
struct gpio_desc *fwnode_get_named_gpiod(struct fwnode_handle *fwnode,
|
||||
const char *propname)
|
||||
{
|
||||
struct gpio_desc *desc = ERR_PTR(-ENODEV);
|
||||
bool active_low = false;
|
||||
int ret;
|
||||
|
||||
if (!fwnode)
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
if (is_of_node(fwnode)) {
|
||||
enum of_gpio_flags flags;
|
||||
|
||||
desc = of_get_named_gpiod_flags(of_node(fwnode), propname, 0,
|
||||
&flags);
|
||||
if (!IS_ERR(desc))
|
||||
active_low = flags & OF_GPIO_ACTIVE_LOW;
|
||||
} else if (is_acpi_node(fwnode)) {
|
||||
struct acpi_gpio_info info;
|
||||
|
||||
desc = acpi_get_gpiod_by_index(acpi_node(fwnode), propname, 0,
|
||||
&info);
|
||||
if (!IS_ERR(desc))
|
||||
active_low = info.active_low;
|
||||
}
|
||||
|
||||
if (IS_ERR(desc))
|
||||
return desc;
|
||||
|
||||
ret = gpiod_request(desc, NULL);
|
||||
if (ret)
|
||||
return ERR_PTR(ret);
|
||||
|
||||
/* Only value flag can be set from both DT and ACPI is active_low */
|
||||
if (active_low)
|
||||
set_bit(FLAG_ACTIVE_LOW, &desc->flags);
|
||||
|
||||
return desc;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(fwnode_get_named_gpiod);
|
||||
|
||||
/**
|
||||
* gpiod_get_index_optional - obtain an optional GPIO from a multi-index GPIO
|
||||
* function
|
||||
|
||||
@@ -34,7 +34,8 @@ void acpi_gpiochip_remove(struct gpio_chip *chip);
|
||||
void acpi_gpiochip_request_interrupts(struct gpio_chip *chip);
|
||||
void acpi_gpiochip_free_interrupts(struct gpio_chip *chip);
|
||||
|
||||
struct gpio_desc *acpi_get_gpiod_by_index(struct device *dev, int index,
|
||||
struct gpio_desc *acpi_get_gpiod_by_index(struct acpi_device *adev,
|
||||
const char *propname, int index,
|
||||
struct acpi_gpio_info *info);
|
||||
#else
|
||||
static inline void acpi_gpiochip_add(struct gpio_chip *chip) { }
|
||||
@@ -47,8 +48,8 @@ static inline void
|
||||
acpi_gpiochip_free_interrupts(struct gpio_chip *chip) { }
|
||||
|
||||
static inline struct gpio_desc *
|
||||
acpi_get_gpiod_by_index(struct device *dev, int index,
|
||||
struct acpi_gpio_info *info)
|
||||
acpi_get_gpiod_by_index(struct acpi_device *adev, const char *propname,
|
||||
int index, struct acpi_gpio_info *info)
|
||||
{
|
||||
return ERR_PTR(-ENOSYS);
|
||||
}
|
||||
|
||||
@@ -23,10 +23,9 @@
|
||||
#include <linux/ioport.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
#include <linux/gpio_keys.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_platform.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/property.h>
|
||||
|
||||
#define DRV_NAME "gpio-keys-polled"
|
||||
|
||||
@@ -51,15 +50,14 @@ static void gpio_keys_polled_check_state(struct input_dev *input,
|
||||
int state;
|
||||
|
||||
if (bdata->can_sleep)
|
||||
state = !!gpio_get_value_cansleep(button->gpio);
|
||||
state = !!gpiod_get_value_cansleep(button->gpiod);
|
||||
else
|
||||
state = !!gpio_get_value(button->gpio);
|
||||
state = !!gpiod_get_value(button->gpiod);
|
||||
|
||||
if (state != bdata->last_state) {
|
||||
unsigned int type = button->type ?: EV_KEY;
|
||||
|
||||
input_event(input, type, button->code,
|
||||
!!(state ^ button->active_low));
|
||||
input_event(input, type, button->code, state);
|
||||
input_sync(input);
|
||||
bdata->count = 0;
|
||||
bdata->last_state = state;
|
||||
@@ -102,21 +100,15 @@ static void gpio_keys_polled_close(struct input_polled_dev *dev)
|
||||
pdata->disable(bdev->dev);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct device *dev)
|
||||
{
|
||||
struct device_node *node, *pp;
|
||||
struct gpio_keys_platform_data *pdata;
|
||||
struct gpio_keys_button *button;
|
||||
struct fwnode_handle *child;
|
||||
int error;
|
||||
int nbuttons;
|
||||
int i;
|
||||
|
||||
node = dev->of_node;
|
||||
if (!node)
|
||||
return NULL;
|
||||
|
||||
nbuttons = of_get_child_count(node);
|
||||
nbuttons = device_get_child_node_count(dev);
|
||||
if (nbuttons == 0)
|
||||
return NULL;
|
||||
|
||||
@@ -126,52 +118,44 @@ static struct gpio_keys_platform_data *gpio_keys_polled_get_devtree_pdata(struct
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
pdata->buttons = (struct gpio_keys_button *)(pdata + 1);
|
||||
pdata->nbuttons = nbuttons;
|
||||
|
||||
pdata->rep = !!of_get_property(node, "autorepeat", NULL);
|
||||
of_property_read_u32(node, "poll-interval", &pdata->poll_interval);
|
||||
pdata->rep = device_property_present(dev, "autorepeat");
|
||||
device_property_read_u32(dev, "poll-interval", &pdata->poll_interval);
|
||||
|
||||
i = 0;
|
||||
for_each_child_of_node(node, pp) {
|
||||
int gpio;
|
||||
enum of_gpio_flags flags;
|
||||
device_for_each_child_node(dev, child) {
|
||||
struct gpio_desc *desc;
|
||||
|
||||
if (!of_find_property(pp, "gpios", NULL)) {
|
||||
pdata->nbuttons--;
|
||||
dev_warn(dev, "Found button without gpios\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
gpio = of_get_gpio_flags(pp, 0, &flags);
|
||||
if (gpio < 0) {
|
||||
error = gpio;
|
||||
desc = devm_get_gpiod_from_child(dev, child);
|
||||
if (IS_ERR(desc)) {
|
||||
error = PTR_ERR(desc);
|
||||
if (error != -EPROBE_DEFER)
|
||||
dev_err(dev,
|
||||
"Failed to get gpio flags, error: %d\n",
|
||||
error);
|
||||
fwnode_handle_put(child);
|
||||
return ERR_PTR(error);
|
||||
}
|
||||
|
||||
button = &pdata->buttons[i++];
|
||||
button = &pdata->buttons[pdata->nbuttons++];
|
||||
button->gpiod = desc;
|
||||
|
||||
button->gpio = gpio;
|
||||
button->active_low = flags & OF_GPIO_ACTIVE_LOW;
|
||||
|
||||
if (of_property_read_u32(pp, "linux,code", &button->code)) {
|
||||
dev_err(dev, "Button without keycode: 0x%x\n",
|
||||
button->gpio);
|
||||
if (fwnode_property_read_u32(child, "linux,code", &button->code)) {
|
||||
dev_err(dev, "Button without keycode: %d\n",
|
||||
pdata->nbuttons - 1);
|
||||
fwnode_handle_put(child);
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
button->desc = of_get_property(pp, "label", NULL);
|
||||
fwnode_property_read_string(child, "label", &button->desc);
|
||||
|
||||
if (of_property_read_u32(pp, "linux,input-type", &button->type))
|
||||
if (fwnode_property_read_u32(child, "linux,input-type",
|
||||
&button->type))
|
||||
button->type = EV_KEY;
|
||||
|
||||
button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);
|
||||
button->wakeup = fwnode_property_present(child, "gpio-key,wakeup");
|
||||
|
||||
if (of_property_read_u32(pp, "debounce-interval",
|
||||
&button->debounce_interval))
|
||||
if (fwnode_property_read_u32(child, "debounce-interval",
|
||||
&button->debounce_interval))
|
||||
button->debounce_interval = 5;
|
||||
}
|
||||
|
||||
@@ -187,15 +171,6 @@ static const struct of_device_id gpio_keys_polled_of_match[] = {
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, gpio_keys_polled_of_match);
|
||||
|
||||
#else
|
||||
|
||||
static inline struct gpio_keys_platform_data *
|
||||
gpio_keys_polled_get_devtree_pdata(struct device *dev)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
#endif
|
||||
|
||||
static int gpio_keys_polled_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
@@ -259,7 +234,6 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
|
||||
for (i = 0; i < pdata->nbuttons; i++) {
|
||||
struct gpio_keys_button *button = &pdata->buttons[i];
|
||||
struct gpio_keys_button_data *bdata = &bdev->data[i];
|
||||
unsigned int gpio = button->gpio;
|
||||
unsigned int type = button->type ?: EV_KEY;
|
||||
|
||||
if (button->wakeup) {
|
||||
@@ -267,15 +241,31 @@ static int gpio_keys_polled_probe(struct platform_device *pdev)
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
error = devm_gpio_request_one(&pdev->dev, gpio, GPIOF_IN,
|
||||
button->desc ? : DRV_NAME);
|
||||
if (error) {
|
||||
dev_err(dev, "unable to claim gpio %u, err=%d\n",
|
||||
gpio, error);
|
||||
return error;
|
||||
/*
|
||||
* Legacy GPIO number so request the GPIO here and
|
||||
* convert it to descriptor.
|
||||
*/
|
||||
if (!button->gpiod && gpio_is_valid(button->gpio)) {
|
||||
unsigned flags = 0;
|
||||
|
||||
if (button->active_low)
|
||||
flags |= GPIOF_ACTIVE_LOW;
|
||||
|
||||
error = devm_gpio_request_one(&pdev->dev, button->gpio,
|
||||
flags, button->desc ? : DRV_NAME);
|
||||
if (error) {
|
||||
dev_err(dev, "unable to claim gpio %u, err=%d\n",
|
||||
button->gpio, error);
|
||||
return error;
|
||||
}
|
||||
|
||||
button->gpiod = gpio_to_desc(button->gpio);
|
||||
}
|
||||
|
||||
bdata->can_sleep = gpio_cansleep(gpio);
|
||||
if (IS_ERR(button->gpiod))
|
||||
return PTR_ERR(button->gpiod);
|
||||
|
||||
bdata->can_sleep = gpiod_cansleep(button->gpiod);
|
||||
bdata->last_state = -1;
|
||||
bdata->threshold = DIV_ROUND_UP(button->debounce_interval,
|
||||
pdata->poll_interval);
|
||||
@@ -308,7 +298,7 @@ static struct platform_driver gpio_keys_polled_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.owner = THIS_MODULE,
|
||||
.of_match_table = of_match_ptr(gpio_keys_polled_of_match),
|
||||
.of_match_table = gpio_keys_polled_of_match,
|
||||
},
|
||||
};
|
||||
module_platform_driver(gpio_keys_polled_driver);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user