gpio: updates for v5.5
- add MODULE_ALIAS() for bd70528 (makes it possible to autoload the module from user-space) - use proper irc_chip names in gpio-em and gpio-rcar - expose the line bias settings to user-space in the form of new request flags - expose a new ioctl() to user-space which allows to change certain proprties of requested lines without releasing them first - various updates for gpio-tegra186: debounce support, code simplification and interrupt routing - use platform_get_irq() in gpio-em for some code shrinkage - remove leftovers after recent gpio-mmio changes -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEFp3rbAvDxGAT0sefEacuoBRx13IFAl3MJV8ACgkQEacuoBRx 13KsKhAAp49dloPr8UuGbmFOJBlhk22xJdZmQa8+zxjDt3e8oh1PXKtYJzg4CoX1 2vG/nnKHtIp1CRmGG2Ls+sIgi/HNQNUEqvPM4wlSHbdG9GH40L/VTEWTa4w00VNY lbJK+N8GWop50LCLGL6I7l5mVkxXAoygrUKkCJ8xXoFNXwRNXWQdhXWhWTQOzjp3 lW+Abx92ZlEEOZklFWqnGPZSQcUWmDXXfI5hqljJcPPYChh7U3/zd5Izhy+2lASq OiIWSQUBox9Izvj3moXZRwo2giEvBSUd4DqRTHejGIqm6OJewUV2QQvteWDURlPQ xEF1DAtvaqvvuql2suwu9VlSx6hOcFaaI/yWUNYvynb7Qs5TIMOhm+E3lWK7D5yS Ay1WyRslp3kxw5fQdZcT1Gdda2FIzz+Icx0DWXyGs/WbDyaPswlX8xbr9pXh1rzG jyNHOUsGLmMBLEVFaUwNj3a/CQQvX0r4Ojkd5WbcRFODy1zeGukOTvOtL42b6pPZ MtxIBvDtUmYDrZsdg27Jie9FUrDYJ5STmzTaKYh3dH/Zxe2Ei0Q3OGE4Tf2kFrOL XZBlBac5OhUDslB05JayF3O/11qjaJ2srPWaiJ4IrtbTcZAjTJz3gqON1Wc6xcGe 1gAjUK5fUST2IzylnFC7n3/S/daIxTSd+Up49QkmQGPVK1MxR3E= =qZW7 -----END PGP SIGNATURE----- Merge tag 'gpio-v5.5-updates-for-linus-part-2' of git://git.kernel.org/pub/scm/linux/kernel/git/brgl/linux into devel gpio: updates for v5.5 - add MODULE_ALIAS() for bd70528 (makes it possible to autoload the module from user-space) - use proper irc_chip names in gpio-em and gpio-rcar - expose the line bias settings to user-space in the form of new request flags - expose a new ioctl() to user-space which allows to change certain proprties of requested lines without releasing them first - various updates for gpio-tegra186: debounce support, code simplification and interrupt routing - use platform_get_irq() in gpio-em for some code shrinkage - remove leftovers after recent gpio-mmio changes
This commit is contained in:
commit
94fc6702d9
|
@ -127,6 +127,7 @@ parameter is applicable::
|
|||
NET Appropriate network support is enabled.
|
||||
NUMA NUMA support is enabled.
|
||||
NFS Appropriate NFS support is enabled.
|
||||
OF Devicetree is enabled.
|
||||
OSS OSS sound support is enabled.
|
||||
PV_OPS A paravirtualized kernel is enabled.
|
||||
PARIDE The ParIDE (parallel port IDE) subsystem is enabled.
|
||||
|
|
|
@ -3194,6 +3194,12 @@
|
|||
This can be set from sysctl after boot.
|
||||
See Documentation/admin-guide/sysctl/vm.rst for details.
|
||||
|
||||
of_devlink [OF, KNL] Create device links between consumer and
|
||||
supplier devices by scanning the devictree to infer the
|
||||
consumer/supplier relationships. A consumer device
|
||||
will not be probed until all the supplier devices have
|
||||
probed successfully.
|
||||
|
||||
ohci1394_dma=early [HW] enable debugging via the ohci1394 driver.
|
||||
See Documentation/debugging-via-ohci1394.txt for more
|
||||
info.
|
||||
|
|
|
@ -281,7 +281,8 @@ State machine
|
|||
:c:func:`driver_bound()`.)
|
||||
|
||||
* Before a consumer device is probed, presence of supplier drivers is
|
||||
verified by checking that links to suppliers are in ``DL_STATE_AVAILABLE``
|
||||
verified by checking the consumer device is not in the wait_for_suppliers
|
||||
list and by checking that links to suppliers are in ``DL_STATE_AVAILABLE``
|
||||
state. The state of the links is updated to ``DL_STATE_CONSUMER_PROBE``.
|
||||
(Call to :c:func:`device_links_check_suppliers()` from
|
||||
:c:func:`really_probe()`.)
|
||||
|
|
|
@ -316,6 +316,10 @@ IOMAP
|
|||
devm_ioremap_nocache()
|
||||
devm_ioremap_wc()
|
||||
devm_ioremap_resource() : checks resource, requests memory region, ioremaps
|
||||
devm_ioremap_resource_wc()
|
||||
devm_platform_ioremap_resource() : calls devm_ioremap_resource() for platform device
|
||||
devm_platform_ioremap_resource_wc()
|
||||
devm_platform_ioremap_resource_byname()
|
||||
devm_iounmap()
|
||||
pcim_iomap()
|
||||
pcim_iomap_regions() : do request_region() and iomap() on multiple BARs
|
||||
|
|
|
@ -169,6 +169,49 @@ A driver's probe() may return a negative errno value to indicate that
|
|||
the driver did not bind to this device, in which case it should have
|
||||
released all resources it allocated::
|
||||
|
||||
void (*sync_state)(struct device *dev);
|
||||
|
||||
sync_state is called only once for a device. It's called when all the consumer
|
||||
devices of the device have successfully probed. The list of consumers of the
|
||||
device is obtained by looking at the device links connecting that device to its
|
||||
consumer devices.
|
||||
|
||||
The first attempt to call sync_state() is made during late_initcall_sync() to
|
||||
give firmware and drivers time to link devices to each other. During the first
|
||||
attempt at calling sync_state(), if all the consumers of the device at that
|
||||
point in time have already probed successfully, sync_state() is called right
|
||||
away. If there are no consumers of the device during the first attempt, that
|
||||
too is considered as "all consumers of the device have probed" and sync_state()
|
||||
is called right away.
|
||||
|
||||
If during the first attempt at calling sync_state() for a device, there are
|
||||
still consumers that haven't probed successfully, the sync_state() call is
|
||||
postponed and reattempted in the future only when one or more consumers of the
|
||||
device probe successfully. If during the reattempt, the driver core finds that
|
||||
there are one or more consumers of the device that haven't probed yet, then
|
||||
sync_state() call is postponed again.
|
||||
|
||||
A typical use case for sync_state() is to have the kernel cleanly take over
|
||||
management of devices from the bootloader. For example, if a device is left on
|
||||
and at a particular hardware configuration by the bootloader, the device's
|
||||
driver might need to keep the device in the boot configuration until all the
|
||||
consumers of the device have probed. Once all the consumers of the device have
|
||||
probed, the device's driver can synchronize the hardware state of the device to
|
||||
match the aggregated software state requested by all the consumers. Hence the
|
||||
name sync_state().
|
||||
|
||||
While obvious examples of resources that can benefit from sync_state() include
|
||||
resources such as regulator, sync_state() can also be useful for complex
|
||||
resources like IOMMUs. For example, IOMMUs with multiple consumers (devices
|
||||
whose addresses are remapped by the IOMMU) might need to keep their mappings
|
||||
fixed at (or additive to) the boot configuration until all its consumers have
|
||||
probed.
|
||||
|
||||
While the typical use case for sync_state() is to have the kernel cleanly take
|
||||
over management of devices from the bootloader, the usage of sync_state() is
|
||||
not restricted to that. Use it whenever it makes sense to take an action after
|
||||
all the consumers of a device have probed.
|
||||
|
||||
int (*remove) (struct device *dev);
|
||||
|
||||
remove is called to unbind a driver from a device. This may be
|
||||
|
|
|
@ -68,41 +68,49 @@ actually necessary; the debugfs code provides a number of helper functions
|
|||
for simple situations. Files containing a single integer value can be
|
||||
created with any of:
|
||||
|
||||
struct dentry *debugfs_create_u8(const char *name, umode_t mode,
|
||||
struct dentry *parent, u8 *value);
|
||||
struct dentry *debugfs_create_u16(const char *name, umode_t mode,
|
||||
struct dentry *parent, u16 *value);
|
||||
void debugfs_create_u8(const char *name, umode_t mode,
|
||||
struct dentry *parent, u8 *value);
|
||||
void debugfs_create_u16(const char *name, umode_t mode,
|
||||
struct dentry *parent, u16 *value);
|
||||
struct dentry *debugfs_create_u32(const char *name, umode_t mode,
|
||||
struct dentry *parent, u32 *value);
|
||||
struct dentry *debugfs_create_u64(const char *name, umode_t mode,
|
||||
struct dentry *parent, u64 *value);
|
||||
void debugfs_create_u64(const char *name, umode_t mode,
|
||||
struct dentry *parent, u64 *value);
|
||||
|
||||
These files support both reading and writing the given value; if a specific
|
||||
file should not be written to, simply set the mode bits accordingly. The
|
||||
values in these files are in decimal; if hexadecimal is more appropriate,
|
||||
the following functions can be used instead:
|
||||
|
||||
struct dentry *debugfs_create_x8(const char *name, umode_t mode,
|
||||
struct dentry *parent, u8 *value);
|
||||
struct dentry *debugfs_create_x16(const char *name, umode_t mode,
|
||||
struct dentry *parent, u16 *value);
|
||||
struct dentry *debugfs_create_x32(const char *name, umode_t mode,
|
||||
struct dentry *parent, u32 *value);
|
||||
struct dentry *debugfs_create_x64(const char *name, umode_t mode,
|
||||
struct dentry *parent, u64 *value);
|
||||
void debugfs_create_x8(const char *name, umode_t mode,
|
||||
struct dentry *parent, u8 *value);
|
||||
void debugfs_create_x16(const char *name, umode_t mode,
|
||||
struct dentry *parent, u16 *value);
|
||||
void debugfs_create_x32(const char *name, umode_t mode,
|
||||
struct dentry *parent, u32 *value);
|
||||
void debugfs_create_x64(const char *name, umode_t mode,
|
||||
struct dentry *parent, u64 *value);
|
||||
|
||||
These functions are useful as long as the developer knows the size of the
|
||||
value to be exported. Some types can have different widths on different
|
||||
architectures, though, complicating the situation somewhat. There is a
|
||||
function meant to help out in one special case:
|
||||
architectures, though, complicating the situation somewhat. There are
|
||||
functions meant to help out in such special cases:
|
||||
|
||||
struct dentry *debugfs_create_size_t(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
size_t *value);
|
||||
void debugfs_create_size_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, size_t *value);
|
||||
|
||||
As might be expected, this function will create a debugfs file to represent
|
||||
a variable of type size_t.
|
||||
|
||||
Similarly, there are helpers for variables of type unsigned long, in decimal
|
||||
and hexadecimal:
|
||||
|
||||
struct dentry *debugfs_create_ulong(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
unsigned long *value);
|
||||
void debugfs_create_xul(const char *name, umode_t mode,
|
||||
struct dentry *parent, unsigned long *value);
|
||||
|
||||
Boolean values can be placed in debugfs with:
|
||||
|
||||
struct dentry *debugfs_create_bool(const char *name, umode_t mode,
|
||||
|
@ -114,8 +122,8 @@ lower-case values, or 1 or 0. Any other input will be silently ignored.
|
|||
|
||||
Also, atomic_t values can be placed in debugfs with:
|
||||
|
||||
struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, atomic_t *value)
|
||||
void debugfs_create_atomic_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, atomic_t *value)
|
||||
|
||||
A read of this file will get atomic_t values, and a write of this file
|
||||
will set atomic_t values.
|
||||
|
|
|
@ -19,7 +19,6 @@
|
|||
|
||||
struct dtl {
|
||||
struct dtl_entry *buf;
|
||||
struct dentry *file;
|
||||
int cpu;
|
||||
int buf_entries;
|
||||
u64 last_idx;
|
||||
|
@ -320,46 +319,28 @@ static const struct file_operations dtl_fops = {
|
|||
|
||||
static struct dentry *dtl_dir;
|
||||
|
||||
static int dtl_setup_file(struct dtl *dtl)
|
||||
static void dtl_setup_file(struct dtl *dtl)
|
||||
{
|
||||
char name[10];
|
||||
|
||||
sprintf(name, "cpu-%d", dtl->cpu);
|
||||
|
||||
dtl->file = debugfs_create_file(name, 0400, dtl_dir, dtl, &dtl_fops);
|
||||
if (!dtl->file)
|
||||
return -ENOMEM;
|
||||
|
||||
return 0;
|
||||
debugfs_create_file(name, 0400, dtl_dir, dtl, &dtl_fops);
|
||||
}
|
||||
|
||||
static int dtl_init(void)
|
||||
{
|
||||
struct dentry *event_mask_file, *buf_entries_file;
|
||||
int rc, i;
|
||||
int i;
|
||||
|
||||
if (!firmware_has_feature(FW_FEATURE_SPLPAR))
|
||||
return -ENODEV;
|
||||
|
||||
/* set up common debugfs structure */
|
||||
|
||||
rc = -ENOMEM;
|
||||
dtl_dir = debugfs_create_dir("dtl", powerpc_debugfs_root);
|
||||
if (!dtl_dir) {
|
||||
printk(KERN_WARNING "%s: can't create dtl root dir\n",
|
||||
__func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
event_mask_file = debugfs_create_x8("dtl_event_mask", 0600,
|
||||
dtl_dir, &dtl_event_mask);
|
||||
buf_entries_file = debugfs_create_u32("dtl_buf_entries", 0400,
|
||||
dtl_dir, &dtl_buf_entries);
|
||||
|
||||
if (!event_mask_file || !buf_entries_file) {
|
||||
printk(KERN_WARNING "%s: can't create dtl files\n", __func__);
|
||||
goto err_remove_dir;
|
||||
}
|
||||
debugfs_create_x8("dtl_event_mask", 0600, dtl_dir, &dtl_event_mask);
|
||||
debugfs_create_u32("dtl_buf_entries", 0400, dtl_dir, &dtl_buf_entries);
|
||||
|
||||
/* set up the per-cpu log structures */
|
||||
for_each_possible_cpu(i) {
|
||||
|
@ -367,16 +348,9 @@ static int dtl_init(void)
|
|||
spin_lock_init(&dtl->lock);
|
||||
dtl->cpu = i;
|
||||
|
||||
rc = dtl_setup_file(dtl);
|
||||
if (rc)
|
||||
goto err_remove_dir;
|
||||
dtl_setup_file(dtl);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_remove_dir:
|
||||
debugfs_remove_recursive(dtl_dir);
|
||||
err:
|
||||
return rc;
|
||||
}
|
||||
machine_arch_initcall(pseries, dtl_init);
|
||||
|
|
|
@ -129,7 +129,6 @@ static void probe_hcall_exit(void *ignored, unsigned long opcode, long retval,
|
|||
static int __init hcall_inst_init(void)
|
||||
{
|
||||
struct dentry *hcall_root;
|
||||
struct dentry *hcall_file;
|
||||
char cpu_name_buf[CPU_NAME_BUF_SIZE];
|
||||
int cpu;
|
||||
|
||||
|
@ -145,17 +144,12 @@ static int __init hcall_inst_init(void)
|
|||
}
|
||||
|
||||
hcall_root = debugfs_create_dir(HCALL_ROOT_DIR, NULL);
|
||||
if (!hcall_root)
|
||||
return -ENOMEM;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
snprintf(cpu_name_buf, CPU_NAME_BUF_SIZE, "cpu%d", cpu);
|
||||
hcall_file = debugfs_create_file(cpu_name_buf, 0444,
|
||||
hcall_root,
|
||||
per_cpu(hcall_stats, cpu),
|
||||
&hcall_inst_seq_fops);
|
||||
if (!hcall_file)
|
||||
return -ENOMEM;
|
||||
debugfs_create_file(cpu_name_buf, 0444, hcall_root,
|
||||
per_cpu(hcall_stats, cpu),
|
||||
&hcall_inst_seq_fops);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -1998,24 +1998,11 @@ static int __init vpa_debugfs_init(void)
|
|||
return 0;
|
||||
|
||||
vpa_dir = debugfs_create_dir("vpa", powerpc_debugfs_root);
|
||||
if (!vpa_dir) {
|
||||
pr_warn("%s: can't create vpa root dir\n", __func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
/* set up the per-cpu vpa file*/
|
||||
for_each_possible_cpu(i) {
|
||||
struct dentry *d;
|
||||
|
||||
sprintf(name, "cpu-%ld", i);
|
||||
|
||||
d = debugfs_create_file(name, 0400, vpa_dir, (void *)i,
|
||||
&vpa_fops);
|
||||
if (!d) {
|
||||
pr_warn("%s: can't create per-cpu vpa file\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
debugfs_create_file(name, 0400, vpa_dir, (void *)i, &vpa_fops);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
# Makefile for the Linux SuperH-specific device drivers.
|
||||
#
|
||||
|
||||
obj-y += dma/
|
||||
obj-y += dma/ platform_early.o
|
||||
|
||||
obj-$(CONFIG_PCI) += pci/
|
||||
obj-$(CONFIG_SUPERHYWAY) += superhyway/
|
||||
|
|
|
@ -0,0 +1,347 @@
|
|||
// SPDX--License-Identifier: GPL-2.0
|
||||
|
||||
#include <asm/platform_early.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/pm.h>
|
||||
|
||||
static __initdata LIST_HEAD(sh_early_platform_driver_list);
|
||||
static __initdata LIST_HEAD(sh_early_platform_device_list);
|
||||
|
||||
static const struct platform_device_id *
|
||||
platform_match_id(const struct platform_device_id *id,
|
||||
struct platform_device *pdev)
|
||||
{
|
||||
while (id->name[0]) {
|
||||
if (strcmp(pdev->name, id->name) == 0) {
|
||||
pdev->id_entry = id;
|
||||
return id;
|
||||
}
|
||||
id++;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int platform_match(struct device *dev, struct device_driver *drv)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct platform_driver *pdrv = to_platform_driver(drv);
|
||||
|
||||
/* When driver_override is set, only bind to the matching driver */
|
||||
if (pdev->driver_override)
|
||||
return !strcmp(pdev->driver_override, drv->name);
|
||||
|
||||
/* Then try to match against the id table */
|
||||
if (pdrv->id_table)
|
||||
return platform_match_id(pdrv->id_table, pdev) != NULL;
|
||||
|
||||
/* fall-back to driver name match */
|
||||
return (strcmp(pdev->name, drv->name) == 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static void device_pm_init_common(struct device *dev)
|
||||
{
|
||||
if (!dev->power.early_init) {
|
||||
spin_lock_init(&dev->power.lock);
|
||||
dev->power.qos = NULL;
|
||||
dev->power.early_init = true;
|
||||
}
|
||||
}
|
||||
|
||||
static void pm_runtime_early_init(struct device *dev)
|
||||
{
|
||||
dev->power.disable_depth = 1;
|
||||
device_pm_init_common(dev);
|
||||
}
|
||||
#else
|
||||
static void pm_runtime_early_init(struct device *dev) {}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* sh_early_platform_driver_register - register early platform driver
|
||||
* @epdrv: sh_early_platform driver structure
|
||||
* @buf: string passed from early_param()
|
||||
*
|
||||
* Helper function for sh_early_platform_init() / sh_early_platform_init_buffer()
|
||||
*/
|
||||
int __init sh_early_platform_driver_register(struct sh_early_platform_driver *epdrv,
|
||||
char *buf)
|
||||
{
|
||||
char *tmp;
|
||||
int n;
|
||||
|
||||
/* Simply add the driver to the end of the global list.
|
||||
* Drivers will by default be put on the list in compiled-in order.
|
||||
*/
|
||||
if (!epdrv->list.next) {
|
||||
INIT_LIST_HEAD(&epdrv->list);
|
||||
list_add_tail(&epdrv->list, &sh_early_platform_driver_list);
|
||||
}
|
||||
|
||||
/* If the user has specified device then make sure the driver
|
||||
* gets prioritized. The driver of the last device specified on
|
||||
* command line will be put first on the list.
|
||||
*/
|
||||
n = strlen(epdrv->pdrv->driver.name);
|
||||
if (buf && !strncmp(buf, epdrv->pdrv->driver.name, n)) {
|
||||
list_move(&epdrv->list, &sh_early_platform_driver_list);
|
||||
|
||||
/* Allow passing parameters after device name */
|
||||
if (buf[n] == '\0' || buf[n] == ',')
|
||||
epdrv->requested_id = -1;
|
||||
else {
|
||||
epdrv->requested_id = simple_strtoul(&buf[n + 1],
|
||||
&tmp, 10);
|
||||
|
||||
if (buf[n] != '.' || (tmp == &buf[n + 1])) {
|
||||
epdrv->requested_id = EARLY_PLATFORM_ID_ERROR;
|
||||
n = 0;
|
||||
} else
|
||||
n += strcspn(&buf[n + 1], ",") + 1;
|
||||
}
|
||||
|
||||
if (buf[n] == ',')
|
||||
n++;
|
||||
|
||||
if (epdrv->bufsize) {
|
||||
memcpy(epdrv->buffer, &buf[n],
|
||||
min_t(int, epdrv->bufsize, strlen(&buf[n]) + 1));
|
||||
epdrv->buffer[epdrv->bufsize - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sh_early_platform_add_devices - adds a number of early platform devices
|
||||
* @devs: array of early platform devices to add
|
||||
* @num: number of early platform devices in array
|
||||
*
|
||||
* Used by early architecture code to register early platform devices and
|
||||
* their platform data.
|
||||
*/
|
||||
void __init sh_early_platform_add_devices(struct platform_device **devs, int num)
|
||||
{
|
||||
struct device *dev;
|
||||
int i;
|
||||
|
||||
/* simply add the devices to list */
|
||||
for (i = 0; i < num; i++) {
|
||||
dev = &devs[i]->dev;
|
||||
|
||||
if (!dev->devres_head.next) {
|
||||
pm_runtime_early_init(dev);
|
||||
INIT_LIST_HEAD(&dev->devres_head);
|
||||
list_add_tail(&dev->devres_head,
|
||||
&sh_early_platform_device_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* sh_early_platform_driver_register_all - register early platform drivers
|
||||
* @class_str: string to identify early platform driver class
|
||||
*
|
||||
* Used by architecture code to register all early platform drivers
|
||||
* for a certain class. If omitted then only early platform drivers
|
||||
* with matching kernel command line class parameters will be registered.
|
||||
*/
|
||||
void __init sh_early_platform_driver_register_all(char *class_str)
|
||||
{
|
||||
/* The "class_str" parameter may or may not be present on the kernel
|
||||
* command line. If it is present then there may be more than one
|
||||
* matching parameter.
|
||||
*
|
||||
* Since we register our early platform drivers using early_param()
|
||||
* we need to make sure that they also get registered in the case
|
||||
* when the parameter is missing from the kernel command line.
|
||||
*
|
||||
* We use parse_early_options() to make sure the early_param() gets
|
||||
* called at least once. The early_param() may be called more than
|
||||
* once since the name of the preferred device may be specified on
|
||||
* the kernel command line. sh_early_platform_driver_register() handles
|
||||
* this case for us.
|
||||
*/
|
||||
parse_early_options(class_str);
|
||||
}
|
||||
|
||||
/**
|
||||
* sh_early_platform_match - find early platform device matching driver
|
||||
* @epdrv: early platform driver structure
|
||||
* @id: id to match against
|
||||
*/
|
||||
static struct platform_device * __init
|
||||
sh_early_platform_match(struct sh_early_platform_driver *epdrv, int id)
|
||||
{
|
||||
struct platform_device *pd;
|
||||
|
||||
list_for_each_entry(pd, &sh_early_platform_device_list, dev.devres_head)
|
||||
if (platform_match(&pd->dev, &epdrv->pdrv->driver))
|
||||
if (pd->id == id)
|
||||
return pd;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* sh_early_platform_left - check if early platform driver has matching devices
|
||||
* @epdrv: early platform driver structure
|
||||
* @id: return true if id or above exists
|
||||
*/
|
||||
static int __init sh_early_platform_left(struct sh_early_platform_driver *epdrv,
|
||||
int id)
|
||||
{
|
||||
struct platform_device *pd;
|
||||
|
||||
list_for_each_entry(pd, &sh_early_platform_device_list, dev.devres_head)
|
||||
if (platform_match(&pd->dev, &epdrv->pdrv->driver))
|
||||
if (pd->id >= id)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* sh_early_platform_driver_probe_id - probe drivers matching class_str and id
|
||||
* @class_str: string to identify early platform driver class
|
||||
* @id: id to match against
|
||||
* @nr_probe: number of platform devices to successfully probe before exiting
|
||||
*/
|
||||
static int __init sh_early_platform_driver_probe_id(char *class_str,
|
||||
int id,
|
||||
int nr_probe)
|
||||
{
|
||||
struct sh_early_platform_driver *epdrv;
|
||||
struct platform_device *match;
|
||||
int match_id;
|
||||
int n = 0;
|
||||
int left = 0;
|
||||
|
||||
list_for_each_entry(epdrv, &sh_early_platform_driver_list, list) {
|
||||
/* only use drivers matching our class_str */
|
||||
if (strcmp(class_str, epdrv->class_str))
|
||||
continue;
|
||||
|
||||
if (id == -2) {
|
||||
match_id = epdrv->requested_id;
|
||||
left = 1;
|
||||
|
||||
} else {
|
||||
match_id = id;
|
||||
left += sh_early_platform_left(epdrv, id);
|
||||
|
||||
/* skip requested id */
|
||||
switch (epdrv->requested_id) {
|
||||
case EARLY_PLATFORM_ID_ERROR:
|
||||
case EARLY_PLATFORM_ID_UNSET:
|
||||
break;
|
||||
default:
|
||||
if (epdrv->requested_id == id)
|
||||
match_id = EARLY_PLATFORM_ID_UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
switch (match_id) {
|
||||
case EARLY_PLATFORM_ID_ERROR:
|
||||
pr_warn("%s: unable to parse %s parameter\n",
|
||||
class_str, epdrv->pdrv->driver.name);
|
||||
/* fall-through */
|
||||
case EARLY_PLATFORM_ID_UNSET:
|
||||
match = NULL;
|
||||
break;
|
||||
default:
|
||||
match = sh_early_platform_match(epdrv, match_id);
|
||||
}
|
||||
|
||||
if (match) {
|
||||
/*
|
||||
* Set up a sensible init_name to enable
|
||||
* dev_name() and others to be used before the
|
||||
* rest of the driver core is initialized.
|
||||
*/
|
||||
if (!match->dev.init_name && slab_is_available()) {
|
||||
if (match->id != -1)
|
||||
match->dev.init_name =
|
||||
kasprintf(GFP_KERNEL, "%s.%d",
|
||||
match->name,
|
||||
match->id);
|
||||
else
|
||||
match->dev.init_name =
|
||||
kasprintf(GFP_KERNEL, "%s",
|
||||
match->name);
|
||||
|
||||
if (!match->dev.init_name)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (epdrv->pdrv->probe(match))
|
||||
pr_warn("%s: unable to probe %s early.\n",
|
||||
class_str, match->name);
|
||||
else
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n >= nr_probe)
|
||||
break;
|
||||
}
|
||||
|
||||
if (left)
|
||||
return n;
|
||||
else
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* sh_early_platform_driver_probe - probe a class of registered drivers
|
||||
* @class_str: string to identify early platform driver class
|
||||
* @nr_probe: number of platform devices to successfully probe before exiting
|
||||
* @user_only: only probe user specified early platform devices
|
||||
*
|
||||
* Used by architecture code to probe registered early platform drivers
|
||||
* within a certain class. For probe to happen a registered early platform
|
||||
* device matching a registered early platform driver is needed.
|
||||
*/
|
||||
int __init sh_early_platform_driver_probe(char *class_str,
|
||||
int nr_probe,
|
||||
int user_only)
|
||||
{
|
||||
int k, n, i;
|
||||
|
||||
n = 0;
|
||||
for (i = -2; n < nr_probe; i++) {
|
||||
k = sh_early_platform_driver_probe_id(class_str, i, nr_probe - n);
|
||||
|
||||
if (k < 0)
|
||||
break;
|
||||
|
||||
n += k;
|
||||
|
||||
if (user_only)
|
||||
break;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* sh_early_platform_cleanup - clean up early platform code
|
||||
*/
|
||||
static int __init sh_early_platform_cleanup(void)
|
||||
{
|
||||
struct platform_device *pd, *pd2;
|
||||
|
||||
/* clean up the devres list used to chain devices */
|
||||
list_for_each_entry_safe(pd, pd2, &sh_early_platform_device_list,
|
||||
dev.devres_head) {
|
||||
list_del(&pd->dev.devres_head);
|
||||
memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
/*
|
||||
* This must happen once after all early devices are probed but before probing
|
||||
* real platform devices.
|
||||
*/
|
||||
subsys_initcall(sh_early_platform_cleanup);
|
|
@ -0,0 +1,61 @@
|
|||
/* SPDX--License-Identifier: GPL-2.0 */
|
||||
|
||||
#ifndef __PLATFORM_EARLY__
|
||||
#define __PLATFORM_EARLY__
|
||||
|
||||
#include <linux/types.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
struct sh_early_platform_driver {
|
||||
const char *class_str;
|
||||
struct platform_driver *pdrv;
|
||||
struct list_head list;
|
||||
int requested_id;
|
||||
char *buffer;
|
||||
int bufsize;
|
||||
};
|
||||
|
||||
#define EARLY_PLATFORM_ID_UNSET -2
|
||||
#define EARLY_PLATFORM_ID_ERROR -3
|
||||
|
||||
extern int sh_early_platform_driver_register(struct sh_early_platform_driver *epdrv,
|
||||
char *buf);
|
||||
extern void sh_early_platform_add_devices(struct platform_device **devs, int num);
|
||||
|
||||
static inline int is_sh_early_platform_device(struct platform_device *pdev)
|
||||
{
|
||||
return !pdev->dev.driver;
|
||||
}
|
||||
|
||||
extern void sh_early_platform_driver_register_all(char *class_str);
|
||||
extern int sh_early_platform_driver_probe(char *class_str,
|
||||
int nr_probe, int user_only);
|
||||
|
||||
#define sh_early_platform_init(class_string, platdrv) \
|
||||
sh_early_platform_init_buffer(class_string, platdrv, NULL, 0)
|
||||
|
||||
#ifndef MODULE
|
||||
#define sh_early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \
|
||||
static __initdata struct sh_early_platform_driver early_driver = { \
|
||||
.class_str = class_string, \
|
||||
.buffer = buf, \
|
||||
.bufsize = bufsiz, \
|
||||
.pdrv = platdrv, \
|
||||
.requested_id = EARLY_PLATFORM_ID_UNSET, \
|
||||
}; \
|
||||
static int __init sh_early_platform_driver_setup_func(char *buffer) \
|
||||
{ \
|
||||
return sh_early_platform_driver_register(&early_driver, buffer); \
|
||||
} \
|
||||
early_param(class_string, sh_early_platform_driver_setup_func)
|
||||
#else /* MODULE */
|
||||
#define sh_early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \
|
||||
static inline char *sh_early_platform_driver_setup_func(void) \
|
||||
{ \
|
||||
return bufsiz ? buf : NULL; \
|
||||
}
|
||||
#endif /* MODULE */
|
||||
|
||||
#endif /* __PLATFORM_EARLY__ */
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/sh_eth.h>
|
||||
#include <linux/sh_timer.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
enum {
|
||||
UNUSED = 0,
|
||||
|
@ -199,6 +200,6 @@ void __init plat_early_device_setup(void)
|
|||
/* enable CMT clock */
|
||||
__raw_writeb(__raw_readb(STBCR3) & ~0x10, STBCR3);
|
||||
|
||||
early_platform_add_devices(sh7619_early_devices,
|
||||
sh_early_platform_add_devices(sh7619_early_devices,
|
||||
ARRAY_SIZE(sh7619_early_devices));
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include <linux/serial.h>
|
||||
#include <linux/serial_sci.h>
|
||||
#include <linux/sh_timer.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
enum {
|
||||
UNUSED = 0,
|
||||
|
@ -169,6 +170,6 @@ static struct platform_device *mxg_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(mxg_early_devices,
|
||||
sh_early_platform_add_devices(mxg_early_devices,
|
||||
ARRAY_SIZE(mxg_early_devices));
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/serial_sci.h>
|
||||
#include <linux/sh_timer.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
enum {
|
||||
UNUSED = 0,
|
||||
|
@ -412,6 +413,6 @@ void __init plat_early_device_setup(void)
|
|||
/* enable MTU2 clock */
|
||||
__raw_writeb(__raw_readb(STBCR3) & ~0x20, STBCR3);
|
||||
|
||||
early_platform_add_devices(sh7201_early_devices,
|
||||
sh_early_platform_add_devices(sh7201_early_devices,
|
||||
ARRAY_SIZE(sh7201_early_devices));
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <linux/serial_sci.h>
|
||||
#include <linux/sh_timer.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
enum {
|
||||
UNUSED = 0,
|
||||
|
@ -349,6 +350,6 @@ void __init plat_early_device_setup(void)
|
|||
/* enable MTU2 clock */
|
||||
__raw_writeb(__raw_readb(STBCR3) & ~0x20, STBCR3);
|
||||
|
||||
early_platform_add_devices(sh7203_early_devices,
|
||||
sh_early_platform_add_devices(sh7203_early_devices,
|
||||
ARRAY_SIZE(sh7203_early_devices));
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/serial_sci.h>
|
||||
#include <linux/sh_timer.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
enum {
|
||||
UNUSED = 0,
|
||||
|
@ -285,6 +286,6 @@ void __init plat_early_device_setup(void)
|
|||
/* enable MTU2 clock */
|
||||
__raw_writeb(__raw_readb(STBCR3) & ~0x20, STBCR3);
|
||||
|
||||
early_platform_add_devices(sh7206_early_devices,
|
||||
sh_early_platform_add_devices(sh7206_early_devices,
|
||||
ARRAY_SIZE(sh7206_early_devices));
|
||||
}
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/usb/r8a66597.h>
|
||||
#include <linux/sh_timer.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
enum {
|
||||
UNUSED = 0,
|
||||
|
@ -546,6 +547,6 @@ static struct platform_device *sh7264_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7264_early_devices,
|
||||
sh_early_platform_add_devices(sh7264_early_devices,
|
||||
ARRAY_SIZE(sh7264_early_devices));
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/usb/r8a66597.h>
|
||||
#include <linux/sh_timer.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
enum {
|
||||
UNUSED = 0,
|
||||
|
@ -562,6 +563,6 @@ static struct platform_device *sh7269_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7269_early_devices,
|
||||
sh_early_platform_add_devices(sh7269_early_devices,
|
||||
ARRAY_SIZE(sh7269_early_devices));
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/irq.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
/* All SH3 devices are equipped with IRQ0->5 (except sh7708) */
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/sh_intc.h>
|
||||
#include <asm/rtc.h>
|
||||
#include <cpu/serial.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
enum {
|
||||
UNUSED = 0,
|
||||
|
@ -178,7 +179,7 @@ static struct platform_device *sh7705_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7705_early_devices,
|
||||
sh_early_platform_add_devices(sh7705_early_devices,
|
||||
ARRAY_SIZE(sh7705_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/sh_timer.h>
|
||||
#include <linux/sh_intc.h>
|
||||
#include <cpu/serial.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
enum {
|
||||
UNUSED = 0,
|
||||
|
@ -230,7 +231,7 @@ static struct platform_device *sh770x_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh770x_early_devices,
|
||||
sh_early_platform_add_devices(sh770x_early_devices,
|
||||
ARRAY_SIZE(sh770x_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/sh_timer.h>
|
||||
#include <linux/sh_intc.h>
|
||||
#include <asm/rtc.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
enum {
|
||||
UNUSED = 0,
|
||||
|
@ -177,7 +178,7 @@ static struct platform_device *sh7710_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7710_early_devices,
|
||||
sh_early_platform_add_devices(sh7710_early_devices,
|
||||
ARRAY_SIZE(sh7710_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/sh_intc.h>
|
||||
#include <linux/usb/ohci_pdriver.h>
|
||||
#include <asm/rtc.h>
|
||||
#include <asm/platform_early.h>
|
||||
#include <cpu/serial.h>
|
||||
|
||||
static struct resource rtc_resources[] = {
|
||||
|
@ -211,7 +212,7 @@ static struct platform_device *sh7720_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7720_early_devices,
|
||||
sh_early_platform_add_devices(sh7720_early_devices,
|
||||
ARRAY_SIZE(sh7720_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/sh_timer.h>
|
||||
#include <linux/sh_intc.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
static struct plat_sci_port scif0_platform_data = {
|
||||
.scscr = SCSCR_REIE,
|
||||
|
@ -76,7 +77,7 @@ static struct platform_device *sh4202_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh4202_early_devices,
|
||||
sh_early_platform_add_devices(sh4202_early_devices,
|
||||
ARRAY_SIZE(sh4202_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/sh_intc.h>
|
||||
#include <linux/serial_sci.h>
|
||||
#include <generated/machtypes.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
static struct resource rtc_resources[] = {
|
||||
[0] = {
|
||||
|
@ -161,15 +162,15 @@ void __init plat_early_device_setup(void)
|
|||
if (mach_is_rts7751r2d()) {
|
||||
scif_platform_data.scscr |= SCSCR_CKE1;
|
||||
dev[0] = &scif_device;
|
||||
early_platform_add_devices(dev, 1);
|
||||
sh_early_platform_add_devices(dev, 1);
|
||||
} else {
|
||||
dev[0] = &sci_device;
|
||||
early_platform_add_devices(dev, 1);
|
||||
sh_early_platform_add_devices(dev, 1);
|
||||
dev[0] = &scif_device;
|
||||
early_platform_add_devices(dev, 1);
|
||||
sh_early_platform_add_devices(dev, 1);
|
||||
}
|
||||
|
||||
early_platform_add_devices(sh7750_early_devices,
|
||||
sh_early_platform_add_devices(sh7750_early_devices,
|
||||
ARRAY_SIZE(sh7750_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/sh_intc.h>
|
||||
#include <linux/serial_sci.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
enum {
|
||||
UNUSED = 0,
|
||||
|
@ -271,7 +272,7 @@ static struct platform_device *sh7760_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7760_early_devices,
|
||||
sh_early_platform_add_devices(sh7760_early_devices,
|
||||
ARRAY_SIZE(sh7760_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/sh_timer.h>
|
||||
#include <linux/sh_intc.h>
|
||||
#include <asm/clock.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
/* Serial */
|
||||
static struct plat_sci_port scif0_platform_data = {
|
||||
|
@ -296,7 +297,7 @@ static struct platform_device *sh7343_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7343_early_devices,
|
||||
sh_early_platform_add_devices(sh7343_early_devices,
|
||||
ARRAY_SIZE(sh7343_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include <linux/sh_intc.h>
|
||||
#include <linux/usb/r8a66597.h>
|
||||
#include <asm/clock.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
static struct plat_sci_port scif0_platform_data = {
|
||||
.scscr = SCSCR_REIE,
|
||||
|
@ -240,7 +241,7 @@ static struct platform_device *sh7366_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7366_early_devices,
|
||||
sh_early_platform_add_devices(sh7366_early_devices,
|
||||
ARRAY_SIZE(sh7366_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <asm/clock.h>
|
||||
#include <asm/mmzone.h>
|
||||
#include <asm/siu.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
#include <cpu/dma-register.h>
|
||||
#include <cpu/sh7722.h>
|
||||
|
@ -512,7 +513,7 @@ static struct platform_device *sh7722_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7722_early_devices,
|
||||
sh_early_platform_add_devices(sh7722_early_devices,
|
||||
ARRAY_SIZE(sh7722_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <asm/clock.h>
|
||||
#include <asm/mmzone.h>
|
||||
#include <asm/platform_early.h>
|
||||
#include <cpu/sh7723.h>
|
||||
|
||||
/* Serial */
|
||||
|
@ -410,7 +411,7 @@ static struct platform_device *sh7723_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7723_early_devices,
|
||||
sh_early_platform_add_devices(sh7723_early_devices,
|
||||
ARRAY_SIZE(sh7723_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@
|
|||
#include <asm/suspend.h>
|
||||
#include <asm/clock.h>
|
||||
#include <asm/mmzone.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
#include <cpu/dma-register.h>
|
||||
#include <cpu/sh7724.h>
|
||||
|
@ -830,7 +831,7 @@ static struct platform_device *sh7724_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7724_early_devices,
|
||||
sh_early_platform_add_devices(sh7724_early_devices,
|
||||
ARRAY_SIZE(sh7724_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <asm/clock.h>
|
||||
#include <asm/irq.h>
|
||||
#include <asm/platform_early.h>
|
||||
#include <cpu/sh7734.h>
|
||||
|
||||
/* SCIF */
|
||||
|
@ -280,7 +281,7 @@ static struct platform_device *sh7734_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7734_early_devices,
|
||||
sh_early_platform_add_devices(sh7734_early_devices,
|
||||
ARRAY_SIZE(sh7734_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include <linux/usb/ohci_pdriver.h>
|
||||
#include <cpu/dma-register.h>
|
||||
#include <cpu/sh7757.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
static struct plat_sci_port scif2_platform_data = {
|
||||
.scscr = SCSCR_REIE,
|
||||
|
@ -767,7 +768,7 @@ static struct platform_device *sh7757_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7757_early_devices,
|
||||
sh_early_platform_add_devices(sh7757_early_devices,
|
||||
ARRAY_SIZE(sh7757_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/serial_sci.h>
|
||||
#include <linux/usb/ohci_pdriver.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
static struct plat_sci_port scif0_platform_data = {
|
||||
.scscr = SCSCR_REIE,
|
||||
|
@ -221,7 +222,7 @@ static struct platform_device *sh7763_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7763_early_devices,
|
||||
sh_early_platform_add_devices(sh7763_early_devices,
|
||||
ARRAY_SIZE(sh7763_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <linux/sh_timer.h>
|
||||
#include <linux/sh_intc.h>
|
||||
#include <linux/io.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
static struct plat_sci_port scif0_platform_data = {
|
||||
.scscr = SCSCR_REIE | SCSCR_TOIE,
|
||||
|
@ -316,7 +317,7 @@ static struct platform_device *sh7770_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7770_early_devices,
|
||||
sh_early_platform_add_devices(sh7770_early_devices,
|
||||
ARRAY_SIZE(sh7770_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include <linux/sh_timer.h>
|
||||
#include <linux/sh_intc.h>
|
||||
#include <cpu/dma-register.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
static struct plat_sci_port scif0_platform_data = {
|
||||
.scscr = SCSCR_REIE | SCSCR_CKE1,
|
||||
|
@ -285,7 +286,7 @@ void __init plat_early_device_setup(void)
|
|||
scif1_platform_data.scscr &= ~SCSCR_CKE1;
|
||||
}
|
||||
|
||||
early_platform_add_devices(sh7780_early_devices,
|
||||
sh_early_platform_add_devices(sh7780_early_devices,
|
||||
ARRAY_SIZE(sh7780_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/sh_timer.h>
|
||||
#include <linux/sh_intc.h>
|
||||
#include <asm/mmzone.h>
|
||||
#include <asm/platform_early.h>
|
||||
#include <cpu/dma-register.h>
|
||||
|
||||
static struct plat_sci_port scif0_platform_data = {
|
||||
|
@ -353,7 +354,7 @@ static struct platform_device *sh7785_early_devices[] __initdata = {
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7785_early_devices,
|
||||
sh_early_platform_add_devices(sh7785_early_devices,
|
||||
ARRAY_SIZE(sh7785_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include <linux/usb/ohci_pdriver.h>
|
||||
#include <cpu/dma-register.h>
|
||||
#include <asm/mmzone.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
static struct plat_sci_port scif0_platform_data = {
|
||||
.scscr = SCSCR_REIE | SCSCR_CKE1,
|
||||
|
@ -834,6 +835,6 @@ arch_initcall(sh7786_devices_setup);
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh7786_early_devices,
|
||||
sh_early_platform_add_devices(sh7786_early_devices,
|
||||
ARRAY_SIZE(sh7786_early_devices));
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/sh_intc.h>
|
||||
#include <cpu/shx3.h>
|
||||
#include <asm/mmzone.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
/*
|
||||
* This intentionally only registers SCIF ports 0, 1, and 3. SCIF 2
|
||||
|
@ -152,7 +153,7 @@ arch_initcall(shx3_devices_setup);
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(shx3_early_devices,
|
||||
sh_early_platform_add_devices(shx3_early_devices,
|
||||
ARRAY_SIZE(shx3_early_devices));
|
||||
}
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include <linux/mm.h>
|
||||
#include <linux/sh_timer.h>
|
||||
#include <asm/addrspace.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
static struct plat_sci_port scif0_platform_data = {
|
||||
.flags = UPF_IOREMAP,
|
||||
|
@ -115,6 +116,6 @@ arch_initcall(sh5_devices_setup);
|
|||
|
||||
void __init plat_early_device_setup(void)
|
||||
{
|
||||
early_platform_add_devices(sh5_early_devices,
|
||||
sh_early_platform_add_devices(sh5_early_devices,
|
||||
ARRAY_SIZE(sh5_early_devices));
|
||||
}
|
||||
|
|
|
@ -44,6 +44,7 @@
|
|||
#include <asm/mmu_context.h>
|
||||
#include <asm/mmzone.h>
|
||||
#include <asm/sparsemem.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
/*
|
||||
* Initialize loops_per_jiffy as 10000000 (1000MIPS).
|
||||
|
@ -328,7 +329,7 @@ void __init setup_arch(char **cmdline_p)
|
|||
sh_mv_setup();
|
||||
|
||||
/* Let earlyprintk output early console messages */
|
||||
early_platform_driver_probe("earlyprintk", 1, 1);
|
||||
sh_early_platform_driver_probe("earlyprintk", 1, 1);
|
||||
|
||||
#ifdef CONFIG_OF_FLATTREE
|
||||
#ifdef CONFIG_USE_BUILTIN_DTB
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <linux/rtc.h>
|
||||
#include <asm/clock.h>
|
||||
#include <asm/rtc.h>
|
||||
#include <asm/platform_early.h>
|
||||
|
||||
static void __init sh_late_time_init(void)
|
||||
{
|
||||
|
@ -30,8 +31,8 @@ static void __init sh_late_time_init(void)
|
|||
* clocksource and the jiffies clocksource is used transparently
|
||||
* instead. No error handling is necessary here.
|
||||
*/
|
||||
early_platform_driver_register_all("earlytimer");
|
||||
early_platform_driver_probe("earlytimer", 2, 0);
|
||||
sh_early_platform_driver_register_all("earlytimer");
|
||||
sh_early_platform_driver_probe("earlytimer", 2, 0);
|
||||
}
|
||||
|
||||
void __init time_init(void)
|
||||
|
|
|
@ -45,6 +45,10 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup);
|
|||
#endif
|
||||
|
||||
/* Device links support. */
|
||||
static LIST_HEAD(wait_for_suppliers);
|
||||
static DEFINE_MUTEX(wfs_lock);
|
||||
static LIST_HEAD(deferred_sync);
|
||||
static unsigned int defer_sync_state_count = 1;
|
||||
|
||||
#ifdef CONFIG_SRCU
|
||||
static DEFINE_MUTEX(device_links_lock);
|
||||
|
@ -127,6 +131,9 @@ static int device_is_dependent(struct device *dev, void *target)
|
|||
return ret;
|
||||
|
||||
list_for_each_entry(link, &dev->links.consumers, s_node) {
|
||||
if (link->flags == (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
|
||||
continue;
|
||||
|
||||
if (link->consumer == target)
|
||||
return 1;
|
||||
|
||||
|
@ -196,8 +203,11 @@ static int device_reorder_to_tail(struct device *dev, void *not_used)
|
|||
device_pm_move_last(dev);
|
||||
|
||||
device_for_each_child(dev, NULL, device_reorder_to_tail);
|
||||
list_for_each_entry(link, &dev->links.consumers, s_node)
|
||||
list_for_each_entry(link, &dev->links.consumers, s_node) {
|
||||
if (link->flags == (DL_FLAG_SYNC_STATE_ONLY | DL_FLAG_MANAGED))
|
||||
continue;
|
||||
device_reorder_to_tail(link->consumer, NULL);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -224,7 +234,8 @@ void device_pm_move_to_tail(struct device *dev)
|
|||
|
||||
#define DL_MANAGED_LINK_FLAGS (DL_FLAG_AUTOREMOVE_CONSUMER | \
|
||||
DL_FLAG_AUTOREMOVE_SUPPLIER | \
|
||||
DL_FLAG_AUTOPROBE_CONSUMER)
|
||||
DL_FLAG_AUTOPROBE_CONSUMER | \
|
||||
DL_FLAG_SYNC_STATE_ONLY)
|
||||
|
||||
#define DL_ADD_VALID_FLAGS (DL_MANAGED_LINK_FLAGS | DL_FLAG_STATELESS | \
|
||||
DL_FLAG_PM_RUNTIME | DL_FLAG_RPM_ACTIVE)
|
||||
|
@ -292,6 +303,8 @@ struct device_link *device_link_add(struct device *consumer,
|
|||
|
||||
if (!consumer || !supplier || flags & ~DL_ADD_VALID_FLAGS ||
|
||||
(flags & DL_FLAG_STATELESS && flags & DL_MANAGED_LINK_FLAGS) ||
|
||||
(flags & DL_FLAG_SYNC_STATE_ONLY &&
|
||||
flags != DL_FLAG_SYNC_STATE_ONLY) ||
|
||||
(flags & DL_FLAG_AUTOPROBE_CONSUMER &&
|
||||
flags & (DL_FLAG_AUTOREMOVE_CONSUMER |
|
||||
DL_FLAG_AUTOREMOVE_SUPPLIER)))
|
||||
|
@ -312,11 +325,14 @@ struct device_link *device_link_add(struct device *consumer,
|
|||
|
||||
/*
|
||||
* If the supplier has not been fully registered yet or there is a
|
||||
* reverse dependency between the consumer and the supplier already in
|
||||
* the graph, return NULL.
|
||||
* reverse (non-SYNC_STATE_ONLY) dependency between the consumer and
|
||||
* the supplier already in the graph, return NULL. If the link is a
|
||||
* SYNC_STATE_ONLY link, we don't check for reverse dependencies
|
||||
* because it only affects sync_state() callbacks.
|
||||
*/
|
||||
if (!device_pm_initialized(supplier)
|
||||
|| device_is_dependent(consumer, supplier)) {
|
||||
|| (!(flags & DL_FLAG_SYNC_STATE_ONLY) &&
|
||||
device_is_dependent(consumer, supplier))) {
|
||||
link = NULL;
|
||||
goto out;
|
||||
}
|
||||
|
@ -343,9 +359,14 @@ struct device_link *device_link_add(struct device *consumer,
|
|||
}
|
||||
|
||||
if (flags & DL_FLAG_STATELESS) {
|
||||
link->flags |= DL_FLAG_STATELESS;
|
||||
kref_get(&link->kref);
|
||||
goto out;
|
||||
if (link->flags & DL_FLAG_SYNC_STATE_ONLY &&
|
||||
!(link->flags & DL_FLAG_STATELESS)) {
|
||||
link->flags |= DL_FLAG_STATELESS;
|
||||
goto reorder;
|
||||
} else {
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -367,6 +388,12 @@ struct device_link *device_link_add(struct device *consumer,
|
|||
link->flags |= DL_FLAG_MANAGED;
|
||||
device_link_init_status(link, consumer, supplier);
|
||||
}
|
||||
if (link->flags & DL_FLAG_SYNC_STATE_ONLY &&
|
||||
!(flags & DL_FLAG_SYNC_STATE_ONLY)) {
|
||||
link->flags &= ~DL_FLAG_SYNC_STATE_ONLY;
|
||||
goto reorder;
|
||||
}
|
||||
|
||||
goto out;
|
||||
}
|
||||
|
||||
|
@ -406,6 +433,13 @@ struct device_link *device_link_add(struct device *consumer,
|
|||
flags & DL_FLAG_PM_RUNTIME)
|
||||
pm_runtime_resume(supplier);
|
||||
|
||||
if (flags & DL_FLAG_SYNC_STATE_ONLY) {
|
||||
dev_dbg(consumer,
|
||||
"Linked as a sync state only consumer to %s\n",
|
||||
dev_name(supplier));
|
||||
goto out;
|
||||
}
|
||||
reorder:
|
||||
/*
|
||||
* Move the consumer and all of the devices depending on it to the end
|
||||
* of dpm_list and the devices_kset list.
|
||||
|
@ -431,6 +465,70 @@ struct device_link *device_link_add(struct device *consumer,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(device_link_add);
|
||||
|
||||
/**
|
||||
* device_link_wait_for_supplier - Add device to wait_for_suppliers list
|
||||
* @consumer: Consumer device
|
||||
*
|
||||
* Marks the @consumer device as waiting for suppliers to become available by
|
||||
* adding it to the wait_for_suppliers list. The consumer device will never be
|
||||
* probed until it's removed from the wait_for_suppliers list.
|
||||
*
|
||||
* The caller is responsible for adding the links to the supplier devices once
|
||||
* they are available and removing the @consumer device from the
|
||||
* wait_for_suppliers list once links to all the suppliers have been created.
|
||||
*
|
||||
* This function is NOT meant to be called from the probe function of the
|
||||
* consumer but rather from code that creates/adds the consumer device.
|
||||
*/
|
||||
static void device_link_wait_for_supplier(struct device *consumer,
|
||||
bool need_for_probe)
|
||||
{
|
||||
mutex_lock(&wfs_lock);
|
||||
list_add_tail(&consumer->links.needs_suppliers, &wait_for_suppliers);
|
||||
consumer->links.need_for_probe = need_for_probe;
|
||||
mutex_unlock(&wfs_lock);
|
||||
}
|
||||
|
||||
static void device_link_wait_for_mandatory_supplier(struct device *consumer)
|
||||
{
|
||||
device_link_wait_for_supplier(consumer, true);
|
||||
}
|
||||
|
||||
static void device_link_wait_for_optional_supplier(struct device *consumer)
|
||||
{
|
||||
device_link_wait_for_supplier(consumer, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* device_link_add_missing_supplier_links - Add links from consumer devices to
|
||||
* supplier devices, leaving any
|
||||
* consumer with inactive suppliers on
|
||||
* the wait_for_suppliers list
|
||||
*
|
||||
* Loops through all consumers waiting on suppliers and tries to add all their
|
||||
* supplier links. If that succeeds, the consumer device is removed from
|
||||
* wait_for_suppliers list. Otherwise, they are left in the wait_for_suppliers
|
||||
* list. Devices left on the wait_for_suppliers list will not be probed.
|
||||
*
|
||||
* The fwnode add_links callback is expected to return 0 if it has found and
|
||||
* added all the supplier links for the consumer device. It should return an
|
||||
* error if it isn't able to do so.
|
||||
*
|
||||
* The caller of device_link_wait_for_supplier() is expected to call this once
|
||||
* it's aware of potential suppliers becoming available.
|
||||
*/
|
||||
static void device_link_add_missing_supplier_links(void)
|
||||
{
|
||||
struct device *dev, *tmp;
|
||||
|
||||
mutex_lock(&wfs_lock);
|
||||
list_for_each_entry_safe(dev, tmp, &wait_for_suppliers,
|
||||
links.needs_suppliers)
|
||||
if (!fwnode_call_int_op(dev->fwnode, add_links, dev))
|
||||
list_del_init(&dev->links.needs_suppliers);
|
||||
mutex_unlock(&wfs_lock);
|
||||
}
|
||||
|
||||
static void device_link_free(struct device_link *link)
|
||||
{
|
||||
while (refcount_dec_not_one(&link->rpm_active))
|
||||
|
@ -565,10 +663,23 @@ int device_links_check_suppliers(struct device *dev)
|
|||
struct device_link *link;
|
||||
int ret = 0;
|
||||
|
||||
/*
|
||||
* Device waiting for supplier to become available is not allowed to
|
||||
* probe.
|
||||
*/
|
||||
mutex_lock(&wfs_lock);
|
||||
if (!list_empty(&dev->links.needs_suppliers) &&
|
||||
dev->links.need_for_probe) {
|
||||
mutex_unlock(&wfs_lock);
|
||||
return -EPROBE_DEFER;
|
||||
}
|
||||
mutex_unlock(&wfs_lock);
|
||||
|
||||
device_links_write_lock();
|
||||
|
||||
list_for_each_entry(link, &dev->links.suppliers, c_node) {
|
||||
if (!(link->flags & DL_FLAG_MANAGED))
|
||||
if (!(link->flags & DL_FLAG_MANAGED) ||
|
||||
link->flags & DL_FLAG_SYNC_STATE_ONLY)
|
||||
continue;
|
||||
|
||||
if (link->status != DL_STATE_AVAILABLE) {
|
||||
|
@ -584,6 +695,69 @@ int device_links_check_suppliers(struct device *dev)
|
|||
return ret;
|
||||
}
|
||||
|
||||
static void __device_links_supplier_sync_state(struct device *dev)
|
||||
{
|
||||
struct device_link *link;
|
||||
|
||||
if (dev->state_synced)
|
||||
return;
|
||||
|
||||
list_for_each_entry(link, &dev->links.consumers, s_node) {
|
||||
if (!(link->flags & DL_FLAG_MANAGED))
|
||||
continue;
|
||||
if (link->status != DL_STATE_ACTIVE)
|
||||
return;
|
||||
}
|
||||
|
||||
if (dev->bus->sync_state)
|
||||
dev->bus->sync_state(dev);
|
||||
else if (dev->driver && dev->driver->sync_state)
|
||||
dev->driver->sync_state(dev);
|
||||
|
||||
dev->state_synced = true;
|
||||
}
|
||||
|
||||
void device_links_supplier_sync_state_pause(void)
|
||||
{
|
||||
device_links_write_lock();
|
||||
defer_sync_state_count++;
|
||||
device_links_write_unlock();
|
||||
}
|
||||
|
||||
void device_links_supplier_sync_state_resume(void)
|
||||
{
|
||||
struct device *dev, *tmp;
|
||||
|
||||
device_links_write_lock();
|
||||
if (!defer_sync_state_count) {
|
||||
WARN(true, "Unmatched sync_state pause/resume!");
|
||||
goto out;
|
||||
}
|
||||
defer_sync_state_count--;
|
||||
if (defer_sync_state_count)
|
||||
goto out;
|
||||
|
||||
list_for_each_entry_safe(dev, tmp, &deferred_sync, links.defer_sync) {
|
||||
__device_links_supplier_sync_state(dev);
|
||||
list_del_init(&dev->links.defer_sync);
|
||||
}
|
||||
out:
|
||||
device_links_write_unlock();
|
||||
}
|
||||
|
||||
static int sync_state_resume_initcall(void)
|
||||
{
|
||||
device_links_supplier_sync_state_resume();
|
||||
return 0;
|
||||
}
|
||||
late_initcall(sync_state_resume_initcall);
|
||||
|
||||
static void __device_links_supplier_defer_sync(struct device *sup)
|
||||
{
|
||||
if (list_empty(&sup->links.defer_sync))
|
||||
list_add_tail(&sup->links.defer_sync, &deferred_sync);
|
||||
}
|
||||
|
||||
/**
|
||||
* device_links_driver_bound - Update device links after probing its driver.
|
||||
* @dev: Device to update the links for.
|
||||
|
@ -599,6 +773,15 @@ void device_links_driver_bound(struct device *dev)
|
|||
{
|
||||
struct device_link *link;
|
||||
|
||||
/*
|
||||
* If a device probes successfully, it's expected to have created all
|
||||
* the device links it needs to or make new device links as it needs
|
||||
* them. So, it no longer needs to wait on any suppliers.
|
||||
*/
|
||||
mutex_lock(&wfs_lock);
|
||||
list_del_init(&dev->links.needs_suppliers);
|
||||
mutex_unlock(&wfs_lock);
|
||||
|
||||
device_links_write_lock();
|
||||
|
||||
list_for_each_entry(link, &dev->links.consumers, s_node) {
|
||||
|
@ -628,6 +811,11 @@ void device_links_driver_bound(struct device *dev)
|
|||
|
||||
WARN_ON(link->status != DL_STATE_CONSUMER_PROBE);
|
||||
WRITE_ONCE(link->status, DL_STATE_ACTIVE);
|
||||
|
||||
if (defer_sync_state_count)
|
||||
__device_links_supplier_defer_sync(link->supplier);
|
||||
else
|
||||
__device_links_supplier_sync_state(link->supplier);
|
||||
}
|
||||
|
||||
dev->links.status = DL_DEV_DRIVER_BOUND;
|
||||
|
@ -744,6 +932,7 @@ void device_links_driver_cleanup(struct device *dev)
|
|||
WRITE_ONCE(link->status, DL_STATE_DORMANT);
|
||||
}
|
||||
|
||||
list_del_init(&dev->links.defer_sync);
|
||||
__device_links_no_driver(dev);
|
||||
|
||||
device_links_write_unlock();
|
||||
|
@ -813,7 +1002,8 @@ void device_links_unbind_consumers(struct device *dev)
|
|||
list_for_each_entry(link, &dev->links.consumers, s_node) {
|
||||
enum device_link_state status;
|
||||
|
||||
if (!(link->flags & DL_FLAG_MANAGED))
|
||||
if (!(link->flags & DL_FLAG_MANAGED) ||
|
||||
link->flags & DL_FLAG_SYNC_STATE_ONLY)
|
||||
continue;
|
||||
|
||||
status = link->status;
|
||||
|
@ -849,6 +1039,10 @@ static void device_links_purge(struct device *dev)
|
|||
{
|
||||
struct device_link *link, *ln;
|
||||
|
||||
mutex_lock(&wfs_lock);
|
||||
list_del(&dev->links.needs_suppliers);
|
||||
mutex_unlock(&wfs_lock);
|
||||
|
||||
/*
|
||||
* Delete all of the remaining links from this device to any other
|
||||
* devices (either consumers or suppliers).
|
||||
|
@ -1713,6 +1907,8 @@ void device_initialize(struct device *dev)
|
|||
#endif
|
||||
INIT_LIST_HEAD(&dev->links.consumers);
|
||||
INIT_LIST_HEAD(&dev->links.suppliers);
|
||||
INIT_LIST_HEAD(&dev->links.needs_suppliers);
|
||||
INIT_LIST_HEAD(&dev->links.defer_sync);
|
||||
dev->links.status = DL_DEV_NO_DRIVER;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(device_initialize);
|
||||
|
@ -2101,7 +2297,7 @@ int device_add(struct device *dev)
|
|||
struct device *parent;
|
||||
struct kobject *kobj;
|
||||
struct class_interface *class_intf;
|
||||
int error = -EINVAL;
|
||||
int error = -EINVAL, fw_ret;
|
||||
struct kobject *glue_dir = NULL;
|
||||
|
||||
dev = get_device(dev);
|
||||
|
@ -2199,6 +2395,32 @@ int device_add(struct device *dev)
|
|||
BUS_NOTIFY_ADD_DEVICE, dev);
|
||||
|
||||
kobject_uevent(&dev->kobj, KOBJ_ADD);
|
||||
|
||||
if (dev->fwnode && !dev->fwnode->dev)
|
||||
dev->fwnode->dev = dev;
|
||||
|
||||
/*
|
||||
* Check if any of the other devices (consumers) have been waiting for
|
||||
* this device (supplier) to be added so that they can create a device
|
||||
* link to it.
|
||||
*
|
||||
* This needs to happen after device_pm_add() because device_link_add()
|
||||
* requires the supplier be registered before it's called.
|
||||
*
|
||||
* But this also needs to happe before bus_probe_device() to make sure
|
||||
* waiting consumers can link to it before the driver is bound to the
|
||||
* device and the driver sync_state callback is called for this device.
|
||||
*/
|
||||
device_link_add_missing_supplier_links();
|
||||
|
||||
if (fwnode_has_op(dev->fwnode, add_links)) {
|
||||
fw_ret = fwnode_call_int_op(dev->fwnode, add_links, dev);
|
||||
if (fw_ret == -ENODEV)
|
||||
device_link_wait_for_mandatory_supplier(dev);
|
||||
else if (fw_ret)
|
||||
device_link_wait_for_optional_supplier(dev);
|
||||
}
|
||||
|
||||
bus_probe_device(dev);
|
||||
if (parent)
|
||||
klist_add_tail(&dev->p->knode_parent,
|
||||
|
@ -2343,6 +2565,9 @@ void device_del(struct device *dev)
|
|||
kill_device(dev);
|
||||
device_unlock(dev);
|
||||
|
||||
if (dev->fwnode && dev->fwnode->dev == dev)
|
||||
dev->fwnode->dev = NULL;
|
||||
|
||||
/* Notify clients of device removal. This call must come
|
||||
* before dpm_sysfs_remove().
|
||||
*/
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
*
|
||||
* Copyright (c) 2003 Manuel Estrada Sainz
|
||||
*
|
||||
* Please see Documentation/firmware_class/ for more information.
|
||||
* Please see Documentation/driver-api/firmware/ for more information.
|
||||
*
|
||||
*/
|
||||
|
||||
|
@ -504,6 +504,7 @@ fw_get_filesystem_firmware(struct device *device, struct fw_priv *fw_priv,
|
|||
path);
|
||||
continue;
|
||||
}
|
||||
dev_dbg(device, "Loading firmware from %s\n", path);
|
||||
if (decompress) {
|
||||
dev_dbg(device, "f/w decompressing %s\n",
|
||||
fw_priv->fw_name);
|
||||
|
|
|
@ -60,6 +60,7 @@ struct resource *platform_get_resource(struct platform_device *dev,
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(platform_get_resource);
|
||||
|
||||
#ifdef CONFIG_HAS_IOMEM
|
||||
/**
|
||||
* devm_platform_ioremap_resource - call devm_ioremap_resource() for a platform
|
||||
* device
|
||||
|
@ -68,7 +69,6 @@ EXPORT_SYMBOL_GPL(platform_get_resource);
|
|||
* resource management
|
||||
* @index: resource index
|
||||
*/
|
||||
#ifdef CONFIG_HAS_IOMEM
|
||||
void __iomem *devm_platform_ioremap_resource(struct platform_device *pdev,
|
||||
unsigned int index)
|
||||
{
|
||||
|
@ -78,9 +78,63 @@ void __iomem *devm_platform_ioremap_resource(struct platform_device *pdev,
|
|||
return devm_ioremap_resource(&pdev->dev, res);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource);
|
||||
|
||||
/**
|
||||
* devm_platform_ioremap_resource_wc - write-combined variant of
|
||||
* devm_platform_ioremap_resource()
|
||||
*
|
||||
* @pdev: platform device to use both for memory resource lookup as well as
|
||||
* resource management
|
||||
* @index: resource index
|
||||
*/
|
||||
void __iomem *devm_platform_ioremap_resource_wc(struct platform_device *pdev,
|
||||
unsigned int index)
|
||||
{
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, index);
|
||||
return devm_ioremap_resource_wc(&pdev->dev, res);
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_platform_ioremap_resource_byname - call devm_ioremap_resource for
|
||||
* a platform device, retrieve the
|
||||
* resource by name
|
||||
*
|
||||
* @pdev: platform device to use both for memory resource lookup as well as
|
||||
* resource management
|
||||
* @name: name of the resource
|
||||
*/
|
||||
void __iomem *
|
||||
devm_platform_ioremap_resource_byname(struct platform_device *pdev,
|
||||
const char *name)
|
||||
{
|
||||
struct resource *res;
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, name);
|
||||
return devm_ioremap_resource(&pdev->dev, res);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_platform_ioremap_resource_byname);
|
||||
#endif /* CONFIG_HAS_IOMEM */
|
||||
|
||||
static int __platform_get_irq(struct platform_device *dev, unsigned int num)
|
||||
/**
|
||||
* platform_get_irq_optional - get an optional IRQ for a device
|
||||
* @dev: platform device
|
||||
* @num: IRQ number index
|
||||
*
|
||||
* Gets an IRQ for a platform device. Device drivers should check the return
|
||||
* value for errors so as to not pass a negative integer value to the
|
||||
* request_irq() APIs. This is the same as platform_get_irq(), except that it
|
||||
* does not print an error message if an IRQ can not be obtained.
|
||||
*
|
||||
* Example:
|
||||
* int irq = platform_get_irq_optional(pdev, 0);
|
||||
* if (irq < 0)
|
||||
* return irq;
|
||||
*
|
||||
* Return: IRQ number on success, negative error number on failure.
|
||||
*/
|
||||
int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
|
||||
{
|
||||
#ifdef CONFIG_SPARC
|
||||
/* sparc does not have irqs represented as IORESOURCE_IRQ resources */
|
||||
|
@ -144,6 +198,7 @@ static int __platform_get_irq(struct platform_device *dev, unsigned int num)
|
|||
return -ENXIO;
|
||||
#endif
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_get_irq_optional);
|
||||
|
||||
/**
|
||||
* platform_get_irq - get an IRQ for a device
|
||||
|
@ -165,7 +220,7 @@ int platform_get_irq(struct platform_device *dev, unsigned int num)
|
|||
{
|
||||
int ret;
|
||||
|
||||
ret = __platform_get_irq(dev, num);
|
||||
ret = platform_get_irq_optional(dev, num);
|
||||
if (ret < 0 && ret != -EPROBE_DEFER)
|
||||
dev_err(&dev->dev, "IRQ index %u not found\n", num);
|
||||
|
||||
|
@ -173,29 +228,6 @@ int platform_get_irq(struct platform_device *dev, unsigned int num)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(platform_get_irq);
|
||||
|
||||
/**
|
||||
* platform_get_irq_optional - get an optional IRQ for a device
|
||||
* @dev: platform device
|
||||
* @num: IRQ number index
|
||||
*
|
||||
* Gets an IRQ for a platform device. Device drivers should check the return
|
||||
* value for errors so as to not pass a negative integer value to the
|
||||
* request_irq() APIs. This is the same as platform_get_irq(), except that it
|
||||
* does not print an error message if an IRQ can not be obtained.
|
||||
*
|
||||
* Example:
|
||||
* int irq = platform_get_irq_optional(pdev, 0);
|
||||
* if (irq < 0)
|
||||
* return irq;
|
||||
*
|
||||
* Return: IRQ number on success, negative error number on failure.
|
||||
*/
|
||||
int platform_get_irq_optional(struct platform_device *dev, unsigned int num)
|
||||
{
|
||||
return __platform_get_irq(dev, num);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_get_irq_optional);
|
||||
|
||||
/**
|
||||
* platform_irq_count - Count the number of IRQs a platform device uses
|
||||
* @dev: platform device
|
||||
|
@ -206,7 +238,7 @@ int platform_irq_count(struct platform_device *dev)
|
|||
{
|
||||
int ret, nr = 0;
|
||||
|
||||
while ((ret = __platform_get_irq(dev, nr)) >= 0)
|
||||
while ((ret = platform_get_irq_optional(dev, nr)) >= 0)
|
||||
nr++;
|
||||
|
||||
if (ret == -EPROBE_DEFER)
|
||||
|
@ -1296,8 +1328,6 @@ int __init platform_bus_init(void)
|
|||
{
|
||||
int error;
|
||||
|
||||
early_platform_cleanup();
|
||||
|
||||
error = device_register(&platform_bus);
|
||||
if (error) {
|
||||
put_device(&platform_bus);
|
||||
|
@ -1309,289 +1339,3 @@ int __init platform_bus_init(void)
|
|||
of_platform_register_reconfig_notifier();
|
||||
return error;
|
||||
}
|
||||
|
||||
static __initdata LIST_HEAD(early_platform_driver_list);
|
||||
static __initdata LIST_HEAD(early_platform_device_list);
|
||||
|
||||
/**
|
||||
* early_platform_driver_register - register early platform driver
|
||||
* @epdrv: early_platform driver structure
|
||||
* @buf: string passed from early_param()
|
||||
*
|
||||
* Helper function for early_platform_init() / early_platform_init_buffer()
|
||||
*/
|
||||
int __init early_platform_driver_register(struct early_platform_driver *epdrv,
|
||||
char *buf)
|
||||
{
|
||||
char *tmp;
|
||||
int n;
|
||||
|
||||
/* Simply add the driver to the end of the global list.
|
||||
* Drivers will by default be put on the list in compiled-in order.
|
||||
*/
|
||||
if (!epdrv->list.next) {
|
||||
INIT_LIST_HEAD(&epdrv->list);
|
||||
list_add_tail(&epdrv->list, &early_platform_driver_list);
|
||||
}
|
||||
|
||||
/* If the user has specified device then make sure the driver
|
||||
* gets prioritized. The driver of the last device specified on
|
||||
* command line will be put first on the list.
|
||||
*/
|
||||
n = strlen(epdrv->pdrv->driver.name);
|
||||
if (buf && !strncmp(buf, epdrv->pdrv->driver.name, n)) {
|
||||
list_move(&epdrv->list, &early_platform_driver_list);
|
||||
|
||||
/* Allow passing parameters after device name */
|
||||
if (buf[n] == '\0' || buf[n] == ',')
|
||||
epdrv->requested_id = -1;
|
||||
else {
|
||||
epdrv->requested_id = simple_strtoul(&buf[n + 1],
|
||||
&tmp, 10);
|
||||
|
||||
if (buf[n] != '.' || (tmp == &buf[n + 1])) {
|
||||
epdrv->requested_id = EARLY_PLATFORM_ID_ERROR;
|
||||
n = 0;
|
||||
} else
|
||||
n += strcspn(&buf[n + 1], ",") + 1;
|
||||
}
|
||||
|
||||
if (buf[n] == ',')
|
||||
n++;
|
||||
|
||||
if (epdrv->bufsize) {
|
||||
memcpy(epdrv->buffer, &buf[n],
|
||||
min_t(int, epdrv->bufsize, strlen(&buf[n]) + 1));
|
||||
epdrv->buffer[epdrv->bufsize - 1] = '\0';
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* early_platform_add_devices - adds a number of early platform devices
|
||||
* @devs: array of early platform devices to add
|
||||
* @num: number of early platform devices in array
|
||||
*
|
||||
* Used by early architecture code to register early platform devices and
|
||||
* their platform data.
|
||||
*/
|
||||
void __init early_platform_add_devices(struct platform_device **devs, int num)
|
||||
{
|
||||
struct device *dev;
|
||||
int i;
|
||||
|
||||
/* simply add the devices to list */
|
||||
for (i = 0; i < num; i++) {
|
||||
dev = &devs[i]->dev;
|
||||
|
||||
if (!dev->devres_head.next) {
|
||||
pm_runtime_early_init(dev);
|
||||
INIT_LIST_HEAD(&dev->devres_head);
|
||||
list_add_tail(&dev->devres_head,
|
||||
&early_platform_device_list);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* early_platform_driver_register_all - register early platform drivers
|
||||
* @class_str: string to identify early platform driver class
|
||||
*
|
||||
* Used by architecture code to register all early platform drivers
|
||||
* for a certain class. If omitted then only early platform drivers
|
||||
* with matching kernel command line class parameters will be registered.
|
||||
*/
|
||||
void __init early_platform_driver_register_all(char *class_str)
|
||||
{
|
||||
/* The "class_str" parameter may or may not be present on the kernel
|
||||
* command line. If it is present then there may be more than one
|
||||
* matching parameter.
|
||||
*
|
||||
* Since we register our early platform drivers using early_param()
|
||||
* we need to make sure that they also get registered in the case
|
||||
* when the parameter is missing from the kernel command line.
|
||||
*
|
||||
* We use parse_early_options() to make sure the early_param() gets
|
||||
* called at least once. The early_param() may be called more than
|
||||
* once since the name of the preferred device may be specified on
|
||||
* the kernel command line. early_platform_driver_register() handles
|
||||
* this case for us.
|
||||
*/
|
||||
parse_early_options(class_str);
|
||||
}
|
||||
|
||||
/**
|
||||
* early_platform_match - find early platform device matching driver
|
||||
* @epdrv: early platform driver structure
|
||||
* @id: id to match against
|
||||
*/
|
||||
static struct platform_device * __init
|
||||
early_platform_match(struct early_platform_driver *epdrv, int id)
|
||||
{
|
||||
struct platform_device *pd;
|
||||
|
||||
list_for_each_entry(pd, &early_platform_device_list, dev.devres_head)
|
||||
if (platform_match(&pd->dev, &epdrv->pdrv->driver))
|
||||
if (pd->id == id)
|
||||
return pd;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* early_platform_left - check if early platform driver has matching devices
|
||||
* @epdrv: early platform driver structure
|
||||
* @id: return true if id or above exists
|
||||
*/
|
||||
static int __init early_platform_left(struct early_platform_driver *epdrv,
|
||||
int id)
|
||||
{
|
||||
struct platform_device *pd;
|
||||
|
||||
list_for_each_entry(pd, &early_platform_device_list, dev.devres_head)
|
||||
if (platform_match(&pd->dev, &epdrv->pdrv->driver))
|
||||
if (pd->id >= id)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* early_platform_driver_probe_id - probe drivers matching class_str and id
|
||||
* @class_str: string to identify early platform driver class
|
||||
* @id: id to match against
|
||||
* @nr_probe: number of platform devices to successfully probe before exiting
|
||||
*/
|
||||
static int __init early_platform_driver_probe_id(char *class_str,
|
||||
int id,
|
||||
int nr_probe)
|
||||
{
|
||||
struct early_platform_driver *epdrv;
|
||||
struct platform_device *match;
|
||||
int match_id;
|
||||
int n = 0;
|
||||
int left = 0;
|
||||
|
||||
list_for_each_entry(epdrv, &early_platform_driver_list, list) {
|
||||
/* only use drivers matching our class_str */
|
||||
if (strcmp(class_str, epdrv->class_str))
|
||||
continue;
|
||||
|
||||
if (id == -2) {
|
||||
match_id = epdrv->requested_id;
|
||||
left = 1;
|
||||
|
||||
} else {
|
||||
match_id = id;
|
||||
left += early_platform_left(epdrv, id);
|
||||
|
||||
/* skip requested id */
|
||||
switch (epdrv->requested_id) {
|
||||
case EARLY_PLATFORM_ID_ERROR:
|
||||
case EARLY_PLATFORM_ID_UNSET:
|
||||
break;
|
||||
default:
|
||||
if (epdrv->requested_id == id)
|
||||
match_id = EARLY_PLATFORM_ID_UNSET;
|
||||
}
|
||||
}
|
||||
|
||||
switch (match_id) {
|
||||
case EARLY_PLATFORM_ID_ERROR:
|
||||
pr_warn("%s: unable to parse %s parameter\n",
|
||||
class_str, epdrv->pdrv->driver.name);
|
||||
/* fall-through */
|
||||
case EARLY_PLATFORM_ID_UNSET:
|
||||
match = NULL;
|
||||
break;
|
||||
default:
|
||||
match = early_platform_match(epdrv, match_id);
|
||||
}
|
||||
|
||||
if (match) {
|
||||
/*
|
||||
* Set up a sensible init_name to enable
|
||||
* dev_name() and others to be used before the
|
||||
* rest of the driver core is initialized.
|
||||
*/
|
||||
if (!match->dev.init_name && slab_is_available()) {
|
||||
if (match->id != -1)
|
||||
match->dev.init_name =
|
||||
kasprintf(GFP_KERNEL, "%s.%d",
|
||||
match->name,
|
||||
match->id);
|
||||
else
|
||||
match->dev.init_name =
|
||||
kasprintf(GFP_KERNEL, "%s",
|
||||
match->name);
|
||||
|
||||
if (!match->dev.init_name)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
if (epdrv->pdrv->probe(match))
|
||||
pr_warn("%s: unable to probe %s early.\n",
|
||||
class_str, match->name);
|
||||
else
|
||||
n++;
|
||||
}
|
||||
|
||||
if (n >= nr_probe)
|
||||
break;
|
||||
}
|
||||
|
||||
if (left)
|
||||
return n;
|
||||
else
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/**
|
||||
* early_platform_driver_probe - probe a class of registered drivers
|
||||
* @class_str: string to identify early platform driver class
|
||||
* @nr_probe: number of platform devices to successfully probe before exiting
|
||||
* @user_only: only probe user specified early platform devices
|
||||
*
|
||||
* Used by architecture code to probe registered early platform drivers
|
||||
* within a certain class. For probe to happen a registered early platform
|
||||
* device matching a registered early platform driver is needed.
|
||||
*/
|
||||
int __init early_platform_driver_probe(char *class_str,
|
||||
int nr_probe,
|
||||
int user_only)
|
||||
{
|
||||
int k, n, i;
|
||||
|
||||
n = 0;
|
||||
for (i = -2; n < nr_probe; i++) {
|
||||
k = early_platform_driver_probe_id(class_str, i, nr_probe - n);
|
||||
|
||||
if (k < 0)
|
||||
break;
|
||||
|
||||
n += k;
|
||||
|
||||
if (user_only)
|
||||
break;
|
||||
}
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* early_platform_cleanup - clean up early platform code
|
||||
*/
|
||||
void __init early_platform_cleanup(void)
|
||||
{
|
||||
struct platform_device *pd, *pd2;
|
||||
|
||||
/* clean up the devres list used to chain devices */
|
||||
list_for_each_entry_safe(pd, pd2, &early_platform_device_list,
|
||||
dev.devres_head) {
|
||||
list_del(&pd->dev.devres_head);
|
||||
memset(&pd->dev.devres_head, 0, sizeof(pd->dev.devres_head));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -104,15 +104,12 @@ static const struct attribute_group soc_attr_group = {
|
|||
.is_visible = soc_attribute_mode,
|
||||
};
|
||||
|
||||
static const struct attribute_group *soc_attr_groups[] = {
|
||||
&soc_attr_group,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static void soc_release(struct device *dev)
|
||||
{
|
||||
struct soc_device *soc_dev = container_of(dev, struct soc_device, dev);
|
||||
|
||||
ida_simple_remove(&soc_ida, soc_dev->soc_dev_num);
|
||||
kfree(soc_dev->dev.groups);
|
||||
kfree(soc_dev);
|
||||
}
|
||||
|
||||
|
@ -121,6 +118,7 @@ static struct soc_device_attribute *early_soc_dev_attr;
|
|||
struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr)
|
||||
{
|
||||
struct soc_device *soc_dev;
|
||||
const struct attribute_group **soc_attr_groups;
|
||||
int ret;
|
||||
|
||||
if (!soc_bus_type.p) {
|
||||
|
@ -136,10 +134,18 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr
|
|||
goto out1;
|
||||
}
|
||||
|
||||
soc_attr_groups = kcalloc(3, sizeof(*soc_attr_groups), GFP_KERNEL);
|
||||
if (!soc_attr_groups) {
|
||||
ret = -ENOMEM;
|
||||
goto out2;
|
||||
}
|
||||
soc_attr_groups[0] = &soc_attr_group;
|
||||
soc_attr_groups[1] = soc_dev_attr->custom_attr_group;
|
||||
|
||||
/* Fetch a unique (reclaimable) SOC ID. */
|
||||
ret = ida_simple_get(&soc_ida, 0, 0, GFP_KERNEL);
|
||||
if (ret < 0)
|
||||
goto out2;
|
||||
goto out3;
|
||||
soc_dev->soc_dev_num = ret;
|
||||
|
||||
soc_dev->attr = soc_dev_attr;
|
||||
|
@ -150,15 +156,15 @@ struct soc_device *soc_device_register(struct soc_device_attribute *soc_dev_attr
|
|||
dev_set_name(&soc_dev->dev, "soc%d", soc_dev->soc_dev_num);
|
||||
|
||||
ret = device_register(&soc_dev->dev);
|
||||
if (ret)
|
||||
goto out3;
|
||||
if (ret) {
|
||||
put_device(&soc_dev->dev);
|
||||
return ERR_PTR(ret);
|
||||
}
|
||||
|
||||
return soc_dev;
|
||||
|
||||
out3:
|
||||
ida_simple_remove(&soc_ida, soc_dev->soc_dev_num);
|
||||
put_device(&soc_dev->dev);
|
||||
soc_dev = NULL;
|
||||
kfree(soc_attr_groups);
|
||||
out2:
|
||||
kfree(soc_dev);
|
||||
out1:
|
||||
|
@ -169,8 +175,6 @@ EXPORT_SYMBOL_GPL(soc_device_register);
|
|||
/* Ensure soc_dev->attr is freed prior to calling soc_device_unregister. */
|
||||
void soc_device_unregister(struct soc_device *soc_dev)
|
||||
{
|
||||
ida_simple_remove(&soc_ida, soc_dev->soc_dev_num);
|
||||
|
||||
device_unregister(&soc_dev->dev);
|
||||
early_soc_dev_attr = NULL;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#ifdef CONFIG_SUPERH
|
||||
#include <asm/platform_early.h>
|
||||
#endif
|
||||
|
||||
struct sh_cmt_device;
|
||||
|
||||
/*
|
||||
|
@ -1052,7 +1056,7 @@ static int sh_cmt_probe(struct platform_device *pdev)
|
|||
struct sh_cmt_device *cmt = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
if (!is_early_platform_device(pdev)) {
|
||||
if (!is_sh_early_platform_device(pdev)) {
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
}
|
||||
|
@ -1072,7 +1076,7 @@ static int sh_cmt_probe(struct platform_device *pdev)
|
|||
pm_runtime_idle(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
if (is_early_platform_device(pdev))
|
||||
if (is_sh_early_platform_device(pdev))
|
||||
return 0;
|
||||
|
||||
out:
|
||||
|
@ -1109,7 +1113,10 @@ static void __exit sh_cmt_exit(void)
|
|||
platform_driver_unregister(&sh_cmt_device_driver);
|
||||
}
|
||||
|
||||
early_platform_init("earlytimer", &sh_cmt_device_driver);
|
||||
#ifdef CONFIG_SUPERH
|
||||
sh_early_platform_init("earlytimer", &sh_cmt_device_driver);
|
||||
#endif
|
||||
|
||||
subsys_initcall(sh_cmt_init);
|
||||
module_exit(sh_cmt_exit);
|
||||
|
||||
|
|
|
@ -23,6 +23,10 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#ifdef CONFIG_SUPERH
|
||||
#include <asm/platform_early.h>
|
||||
#endif
|
||||
|
||||
struct sh_mtu2_device;
|
||||
|
||||
struct sh_mtu2_channel {
|
||||
|
@ -442,7 +446,7 @@ static int sh_mtu2_probe(struct platform_device *pdev)
|
|||
struct sh_mtu2_device *mtu = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
if (!is_early_platform_device(pdev)) {
|
||||
if (!is_sh_early_platform_device(pdev)) {
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
}
|
||||
|
@ -462,7 +466,7 @@ static int sh_mtu2_probe(struct platform_device *pdev)
|
|||
pm_runtime_idle(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
if (is_early_platform_device(pdev))
|
||||
if (is_sh_early_platform_device(pdev))
|
||||
return 0;
|
||||
|
||||
out:
|
||||
|
@ -511,7 +515,10 @@ static void __exit sh_mtu2_exit(void)
|
|||
platform_driver_unregister(&sh_mtu2_device_driver);
|
||||
}
|
||||
|
||||
early_platform_init("earlytimer", &sh_mtu2_device_driver);
|
||||
#ifdef CONFIG_SUPERH
|
||||
sh_early_platform_init("earlytimer", &sh_mtu2_device_driver);
|
||||
#endif
|
||||
|
||||
subsys_initcall(sh_mtu2_init);
|
||||
module_exit(sh_mtu2_exit);
|
||||
|
||||
|
|
|
@ -24,6 +24,10 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
#ifdef CONFIG_SUPERH
|
||||
#include <asm/platform_early.h>
|
||||
#endif
|
||||
|
||||
enum sh_tmu_model {
|
||||
SH_TMU,
|
||||
SH_TMU_SH3,
|
||||
|
@ -595,7 +599,7 @@ static int sh_tmu_probe(struct platform_device *pdev)
|
|||
struct sh_tmu_device *tmu = platform_get_drvdata(pdev);
|
||||
int ret;
|
||||
|
||||
if (!is_early_platform_device(pdev)) {
|
||||
if (!is_sh_early_platform_device(pdev)) {
|
||||
pm_runtime_set_active(&pdev->dev);
|
||||
pm_runtime_enable(&pdev->dev);
|
||||
}
|
||||
|
@ -615,7 +619,8 @@ static int sh_tmu_probe(struct platform_device *pdev)
|
|||
pm_runtime_idle(&pdev->dev);
|
||||
return ret;
|
||||
}
|
||||
if (is_early_platform_device(pdev))
|
||||
|
||||
if (is_sh_early_platform_device(pdev))
|
||||
return 0;
|
||||
|
||||
out:
|
||||
|
@ -665,7 +670,10 @@ static void __exit sh_tmu_exit(void)
|
|||
platform_driver_unregister(&sh_tmu_device_driver);
|
||||
}
|
||||
|
||||
early_platform_init("earlytimer", &sh_tmu_device_driver);
|
||||
#ifdef CONFIG_SUPERH
|
||||
sh_early_platform_init("earlytimer", &sh_tmu_device_driver);
|
||||
#endif
|
||||
|
||||
subsys_initcall(sh_tmu_init);
|
||||
module_exit(sh_tmu_exit);
|
||||
|
||||
|
|
|
@ -232,3 +232,4 @@ module_platform_driver(bd70528_gpio);
|
|||
MODULE_AUTHOR("Matti Vaittinen <matti.vaittinen@fi.rohmeurope.com>");
|
||||
MODULE_DESCRIPTION("BD70528 voltage regulator driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:bd70528-gpio");
|
||||
|
|
|
@ -269,13 +269,12 @@ static void em_gio_irq_domain_remove(void *data)
|
|||
static int em_gio_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct em_gio_priv *p;
|
||||
struct resource *irq[2];
|
||||
struct gpio_chip *gpio_chip;
|
||||
struct irq_chip *irq_chip;
|
||||
struct device *dev = &pdev->dev;
|
||||
const char *name = dev_name(dev);
|
||||
unsigned int ngpios;
|
||||
int ret;
|
||||
int irq[2], ret;
|
||||
|
||||
p = devm_kzalloc(dev, sizeof(*p), GFP_KERNEL);
|
||||
if (!p)
|
||||
|
@ -285,13 +284,13 @@ static int em_gio_probe(struct platform_device *pdev)
|
|||
platform_set_drvdata(pdev, p);
|
||||
spin_lock_init(&p->sense_lock);
|
||||
|
||||
irq[0] = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
|
||||
irq[1] = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
|
||||
irq[0] = platform_get_irq(pdev, 0);
|
||||
if (irq[0] < 0)
|
||||
return irq[0];
|
||||
|
||||
if (!irq[0] || !irq[1]) {
|
||||
dev_err(dev, "missing IRQ or IOMEM\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
irq[1] = platform_get_irq(pdev, 1);
|
||||
if (irq[1] < 0)
|
||||
return irq[1];
|
||||
|
||||
p->base0 = devm_platform_ioremap_resource(pdev, 0);
|
||||
if (IS_ERR(p->base0))
|
||||
|
@ -322,7 +321,7 @@ static int em_gio_probe(struct platform_device *pdev)
|
|||
gpio_chip->ngpio = ngpios;
|
||||
|
||||
irq_chip = &p->irq_chip;
|
||||
irq_chip->name = name;
|
||||
irq_chip->name = "gpio-em";
|
||||
irq_chip->irq_mask = em_gio_irq_disable;
|
||||
irq_chip->irq_unmask = em_gio_irq_enable;
|
||||
irq_chip->irq_set_type = em_gio_irq_set_type;
|
||||
|
@ -342,14 +341,12 @@ static int em_gio_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (devm_request_irq(dev, irq[0]->start,
|
||||
em_gio_irq_handler, 0, name, p)) {
|
||||
if (devm_request_irq(dev, irq[0], em_gio_irq_handler, 0, name, p)) {
|
||||
dev_err(dev, "failed to request low IRQ\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (devm_request_irq(dev, irq[1]->start,
|
||||
em_gio_irq_handler, 0, name, p)) {
|
||||
if (devm_request_irq(dev, irq[1], em_gio_irq_handler, 0, name, p)) {
|
||||
dev_err(dev, "failed to request high IRQ\n");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
|
|
@ -365,9 +365,8 @@ static void mrfld_irq_handler(struct irq_desc *desc)
|
|||
chained_irq_exit(irqchip, desc);
|
||||
}
|
||||
|
||||
static int mrfld_irq_init_hw(struct gpio_chip *chip)
|
||||
static void mrfld_irq_init_hw(struct mrfld_gpio *priv)
|
||||
{
|
||||
struct mrfld_gpio *priv = gpiochip_get_data(chip);
|
||||
void __iomem *reg;
|
||||
unsigned int base;
|
||||
|
||||
|
@ -379,8 +378,6 @@ static int mrfld_irq_init_hw(struct gpio_chip *chip)
|
|||
reg = gpio_reg(&priv->chip, base, GFER);
|
||||
writel(0, reg);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const char *mrfld_gpio_get_pinctrl_dev_name(struct mrfld_gpio *priv)
|
||||
|
@ -403,7 +400,6 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
|
|||
{
|
||||
const struct mrfld_gpio_pinrange *range;
|
||||
const char *pinctrl_dev_name;
|
||||
struct gpio_irq_chip *girq;
|
||||
struct mrfld_gpio *priv;
|
||||
u32 gpio_base, irq_base;
|
||||
void __iomem *base;
|
||||
|
@ -451,21 +447,6 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
|
|||
|
||||
raw_spin_lock_init(&priv->lock);
|
||||
|
||||
girq = &priv->chip.irq;
|
||||
girq->chip = &mrfld_irqchip;
|
||||
girq->init_hw = mrfld_irq_init_hw;
|
||||
girq->parent_handler = mrfld_irq_handler;
|
||||
girq->num_parents = 1;
|
||||
girq->parents = devm_kcalloc(&pdev->dev, girq->num_parents,
|
||||
sizeof(*girq->parents),
|
||||
GFP_KERNEL);
|
||||
if (!girq->parents)
|
||||
return -ENOMEM;
|
||||
girq->parents[0] = pdev->irq;
|
||||
girq->first = irq_base;
|
||||
girq->default_type = IRQ_TYPE_NONE;
|
||||
girq->handler = handle_bad_irq;
|
||||
|
||||
pci_set_drvdata(pdev, priv);
|
||||
retval = devm_gpiochip_add_data(&pdev->dev, &priv->chip, priv);
|
||||
if (retval) {
|
||||
|
@ -487,6 +468,18 @@ static int mrfld_gpio_probe(struct pci_dev *pdev, const struct pci_device_id *id
|
|||
}
|
||||
}
|
||||
|
||||
retval = gpiochip_irqchip_add(&priv->chip, &mrfld_irqchip, irq_base,
|
||||
handle_bad_irq, IRQ_TYPE_NONE);
|
||||
if (retval) {
|
||||
dev_err(&pdev->dev, "could not connect irqchip to gpiochip\n");
|
||||
return retval;
|
||||
}
|
||||
|
||||
mrfld_irq_init_hw(priv);
|
||||
|
||||
gpiochip_set_chained_irqchip(&priv->chip, &mrfld_irqchip, pdev->irq,
|
||||
mrfld_irq_handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -386,7 +386,6 @@ static int bgpio_get_dir(struct gpio_chip *gc, unsigned int gpio)
|
|||
if (!(gc->read_reg(gc->reg_dir_in) & bgpio_line2mask(gc, gpio)))
|
||||
return GPIO_LINE_DIRECTION_OUT;
|
||||
|
||||
/* This should not happen */
|
||||
return GPIO_LINE_DIRECTION_IN;
|
||||
}
|
||||
|
||||
|
|
|
@ -141,6 +141,61 @@ static void gpio_mockup_set_multiple(struct gpio_chip *gc,
|
|||
mutex_unlock(&chip->lock);
|
||||
}
|
||||
|
||||
static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip,
|
||||
unsigned int offset, int value)
|
||||
{
|
||||
struct gpio_desc *desc;
|
||||
struct gpio_chip *gc;
|
||||
struct irq_sim *sim;
|
||||
int curr, irq, irq_type;
|
||||
|
||||
gc = &chip->gc;
|
||||
desc = &gc->gpiodev->descs[offset];
|
||||
sim = &chip->irqsim;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
if (test_bit(FLAG_REQUESTED, &desc->flags) &&
|
||||
!test_bit(FLAG_IS_OUT, &desc->flags)) {
|
||||
curr = __gpio_mockup_get(chip, offset);
|
||||
if (curr == value)
|
||||
goto out;
|
||||
|
||||
irq = irq_sim_irqnum(sim, offset);
|
||||
irq_type = irq_get_trigger_type(irq);
|
||||
|
||||
if ((value == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) ||
|
||||
(value == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING)))
|
||||
irq_sim_fire(sim, offset);
|
||||
}
|
||||
|
||||
/* Change the value unless we're actively driving the line. */
|
||||
if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
|
||||
!test_bit(FLAG_IS_OUT, &desc->flags))
|
||||
__gpio_mockup_set(chip, offset, value);
|
||||
|
||||
out:
|
||||
chip->lines[offset].pull = value;
|
||||
mutex_unlock(&chip->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int gpio_mockup_set_config(struct gpio_chip *gc,
|
||||
unsigned int offset, unsigned long config)
|
||||
{
|
||||
struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
|
||||
|
||||
switch (pinconf_to_config_param(config)) {
|
||||
case PIN_CONFIG_BIAS_PULL_UP:
|
||||
return gpio_mockup_apply_pull(chip, offset, 1);
|
||||
case PIN_CONFIG_BIAS_PULL_DOWN:
|
||||
return gpio_mockup_apply_pull(chip, offset, 0);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int gpio_mockup_dirout(struct gpio_chip *gc,
|
||||
unsigned int offset, int value)
|
||||
{
|
||||
|
@ -221,12 +276,8 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file,
|
|||
size_t size, loff_t *ppos)
|
||||
{
|
||||
struct gpio_mockup_dbgfs_private *priv;
|
||||
int rv, val, curr, irq, irq_type;
|
||||
struct gpio_mockup_chip *chip;
|
||||
int rv, val;
|
||||
struct seq_file *sfile;
|
||||
struct gpio_desc *desc;
|
||||
struct gpio_chip *gc;
|
||||
struct irq_sim *sim;
|
||||
|
||||
if (*ppos != 0)
|
||||
return -EINVAL;
|
||||
|
@ -239,35 +290,9 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file,
|
|||
|
||||
sfile = file->private_data;
|
||||
priv = sfile->private;
|
||||
chip = priv->chip;
|
||||
gc = &chip->gc;
|
||||
desc = &gc->gpiodev->descs[priv->offset];
|
||||
sim = &chip->irqsim;
|
||||
|
||||
mutex_lock(&chip->lock);
|
||||
|
||||
if (test_bit(FLAG_REQUESTED, &desc->flags) &&
|
||||
!test_bit(FLAG_IS_OUT, &desc->flags)) {
|
||||
curr = __gpio_mockup_get(chip, priv->offset);
|
||||
if (curr == val)
|
||||
goto out;
|
||||
|
||||
irq = irq_sim_irqnum(sim, priv->offset);
|
||||
irq_type = irq_get_trigger_type(irq);
|
||||
|
||||
if ((val == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) ||
|
||||
(val == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING)))
|
||||
irq_sim_fire(sim, priv->offset);
|
||||
}
|
||||
|
||||
/* Change the value unless we're actively driving the line. */
|
||||
if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
|
||||
!test_bit(FLAG_IS_OUT, &desc->flags))
|
||||
__gpio_mockup_set(chip, priv->offset, val);
|
||||
|
||||
out:
|
||||
chip->lines[priv->offset].pull = val;
|
||||
mutex_unlock(&chip->lock);
|
||||
rv = gpio_mockup_apply_pull(priv->chip, priv->offset, val);
|
||||
if (rv)
|
||||
return rv;
|
||||
|
||||
return size;
|
||||
}
|
||||
|
@ -413,6 +438,7 @@ static int gpio_mockup_probe(struct platform_device *pdev)
|
|||
gc->direction_output = gpio_mockup_dirout;
|
||||
gc->direction_input = gpio_mockup_dirin;
|
||||
gc->get_direction = gpio_mockup_get_direction;
|
||||
gc->set_config = gpio_mockup_set_config;
|
||||
gc->to_irq = gpio_mockup_to_irq;
|
||||
gc->free = gpio_mockup_free;
|
||||
|
||||
|
|
|
@ -776,23 +776,12 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
|
|||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
struct mvebu_pwm *mvpwm;
|
||||
struct resource *res;
|
||||
u32 set;
|
||||
|
||||
if (!of_device_is_compatible(mvchip->chip.of_node,
|
||||
"marvell,armada-370-gpio"))
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* There are only two sets of PWM configuration registers for
|
||||
* all the GPIO lines on those SoCs which this driver reserves
|
||||
* for the first two GPIO chips. So if the resource is missing
|
||||
* we can't treat it as an error.
|
||||
*/
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pwm");
|
||||
if (!res)
|
||||
return 0;
|
||||
|
||||
if (IS_ERR(mvchip->clk))
|
||||
return PTR_ERR(mvchip->clk);
|
||||
|
||||
|
@ -815,7 +804,13 @@ static int mvebu_pwm_probe(struct platform_device *pdev,
|
|||
mvchip->mvpwm = mvpwm;
|
||||
mvpwm->mvchip = mvchip;
|
||||
|
||||
mvpwm->membase = devm_ioremap_resource(dev, res);
|
||||
/*
|
||||
* There are only two sets of PWM configuration registers for
|
||||
* all the GPIO lines on those SoCs which this driver reserves
|
||||
* for the first two GPIO chips. So if the resource is missing
|
||||
* we can't treat it as an error.
|
||||
*/
|
||||
mvpwm->membase = devm_platform_ioremap_resource_byname(pdev, "pwm");
|
||||
if (IS_ERR(mvpwm->membase))
|
||||
return PTR_ERR(mvpwm->membase);
|
||||
|
||||
|
|
|
@ -486,7 +486,7 @@ static int gpio_rcar_probe(struct platform_device *pdev)
|
|||
gpio_chip->ngpio = npins;
|
||||
|
||||
irq_chip = &p->irq_chip;
|
||||
irq_chip->name = name;
|
||||
irq_chip->name = "gpio-rcar";
|
||||
irq_chip->parent_device = dev;
|
||||
irq_chip->irq_mask = gpio_rcar_irq_disable;
|
||||
irq_chip->irq_unmask = gpio_rcar_irq_enable;
|
||||
|
|
|
@ -15,6 +15,14 @@
|
|||
#include <dt-bindings/gpio/tegra186-gpio.h>
|
||||
#include <dt-bindings/gpio/tegra194-gpio.h>
|
||||
|
||||
/* security registers */
|
||||
#define TEGRA186_GPIO_CTL_SCR 0x0c
|
||||
#define TEGRA186_GPIO_CTL_SCR_SEC_WEN BIT(28)
|
||||
#define TEGRA186_GPIO_CTL_SCR_SEC_REN BIT(27)
|
||||
|
||||
#define TEGRA186_GPIO_INT_ROUTE_MAPPING(p, x) (0x14 + (p) * 0x20 + (x) * 4)
|
||||
|
||||
/* control registers */
|
||||
#define TEGRA186_GPIO_ENABLE_CONFIG 0x00
|
||||
#define TEGRA186_GPIO_ENABLE_CONFIG_ENABLE BIT(0)
|
||||
#define TEGRA186_GPIO_ENABLE_CONFIG_OUT BIT(1)
|
||||
|
@ -24,6 +32,7 @@
|
|||
#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_DOUBLE_EDGE (0x3 << 2)
|
||||
#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_TYPE_MASK (0x3 << 2)
|
||||
#define TEGRA186_GPIO_ENABLE_CONFIG_TRIGGER_LEVEL BIT(4)
|
||||
#define TEGRA186_GPIO_ENABLE_CONFIG_DEBOUNCE BIT(5)
|
||||
#define TEGRA186_GPIO_ENABLE_CONFIG_INTERRUPT BIT(6)
|
||||
|
||||
#define TEGRA186_GPIO_DEBOUNCE_CONTROL 0x04
|
||||
|
@ -44,9 +53,9 @@
|
|||
|
||||
struct tegra_gpio_port {
|
||||
const char *name;
|
||||
unsigned int offset;
|
||||
unsigned int bank;
|
||||
unsigned int port;
|
||||
unsigned int pins;
|
||||
unsigned int irq;
|
||||
};
|
||||
|
||||
struct tegra_gpio_soc {
|
||||
|
@ -64,6 +73,7 @@ struct tegra_gpio {
|
|||
|
||||
const struct tegra_gpio_soc *soc;
|
||||
|
||||
void __iomem *secure;
|
||||
void __iomem *base;
|
||||
};
|
||||
|
||||
|
@ -90,12 +100,15 @@ static void __iomem *tegra186_gpio_get_base(struct tegra_gpio *gpio,
|
|||
unsigned int pin)
|
||||
{
|
||||
const struct tegra_gpio_port *port;
|
||||
unsigned int offset;
|
||||
|
||||
port = tegra186_gpio_get_port(gpio, &pin);
|
||||
if (!port)
|
||||
return NULL;
|
||||
|
||||
return gpio->base + port->offset + pin * 0x20;
|
||||
offset = port->bank * 0x1000 + port->port * 0x200;
|
||||
|
||||
return gpio->base + offset + pin * 0x20;
|
||||
}
|
||||
|
||||
static int tegra186_gpio_get_direction(struct gpio_chip *chip,
|
||||
|
@ -205,6 +218,42 @@ static void tegra186_gpio_set(struct gpio_chip *chip, unsigned int offset,
|
|||
writel(value, base + TEGRA186_GPIO_OUTPUT_VALUE);
|
||||
}
|
||||
|
||||
static int tegra186_gpio_set_config(struct gpio_chip *chip,
|
||||
unsigned int offset,
|
||||
unsigned long config)
|
||||
{
|
||||
struct tegra_gpio *gpio = gpiochip_get_data(chip);
|
||||
u32 debounce, value;
|
||||
void __iomem *base;
|
||||
|
||||
base = tegra186_gpio_get_base(gpio, offset);
|
||||
if (base == NULL)
|
||||
return -ENXIO;
|
||||
|
||||
if (pinconf_to_config_param(config) != PIN_CONFIG_INPUT_DEBOUNCE)
|
||||
return -ENOTSUPP;
|
||||
|
||||
debounce = pinconf_to_config_argument(config);
|
||||
|
||||
/*
|
||||
* The Tegra186 GPIO controller supports a maximum of 255 ms debounce
|
||||
* time.
|
||||
*/
|
||||
if (debounce > 255000)
|
||||
return -EINVAL;
|
||||
|
||||
debounce = DIV_ROUND_UP(debounce, USEC_PER_MSEC);
|
||||
|
||||
value = TEGRA186_GPIO_DEBOUNCE_CONTROL_THRESHOLD(debounce);
|
||||
writel(value, base + TEGRA186_GPIO_DEBOUNCE_CONTROL);
|
||||
|
||||
value = readl(base + TEGRA186_GPIO_ENABLE_CONFIG);
|
||||
value |= TEGRA186_GPIO_ENABLE_CONFIG_DEBOUNCE;
|
||||
writel(value, base + TEGRA186_GPIO_ENABLE_CONFIG);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tegra186_gpio_of_xlate(struct gpio_chip *chip,
|
||||
const struct of_phandle_args *spec,
|
||||
u32 *flags)
|
||||
|
@ -343,12 +392,14 @@ static void tegra186_gpio_irq(struct irq_desc *desc)
|
|||
|
||||
for (i = 0; i < gpio->soc->num_ports; i++) {
|
||||
const struct tegra_gpio_port *port = &gpio->soc->ports[i];
|
||||
void __iomem *base = gpio->base + port->offset;
|
||||
unsigned int pin, irq;
|
||||
unsigned long value;
|
||||
void __iomem *base;
|
||||
|
||||
/* skip ports that are not associated with this controller */
|
||||
if (parent != gpio->irq[port->irq])
|
||||
base = gpio->base + port->bank * 0x1000 + port->port * 0x200;
|
||||
|
||||
/* skip ports that are not associated with this bank */
|
||||
if (parent != gpio->irq[port->bank])
|
||||
goto skip;
|
||||
|
||||
value = readl(base + TEGRA186_GPIO_INTERRUPT_STATUS(1));
|
||||
|
@ -444,13 +495,43 @@ static const struct of_device_id tegra186_pmc_of_match[] = {
|
|||
{ /* sentinel */ }
|
||||
};
|
||||
|
||||
static void tegra186_gpio_init_route_mapping(struct tegra_gpio *gpio)
|
||||
{
|
||||
unsigned int i, j;
|
||||
u32 value;
|
||||
|
||||
for (i = 0; i < gpio->soc->num_ports; i++) {
|
||||
const struct tegra_gpio_port *port = &gpio->soc->ports[i];
|
||||
unsigned int offset, p = port->port;
|
||||
void __iomem *base;
|
||||
|
||||
base = gpio->secure + port->bank * 0x1000 + 0x800;
|
||||
|
||||
value = readl(base + TEGRA186_GPIO_CTL_SCR);
|
||||
|
||||
/*
|
||||
* For controllers that haven't been locked down yet, make
|
||||
* sure to program the default interrupt route mapping.
|
||||
*/
|
||||
if ((value & TEGRA186_GPIO_CTL_SCR_SEC_REN) == 0 &&
|
||||
(value & TEGRA186_GPIO_CTL_SCR_SEC_WEN) == 0) {
|
||||
for (j = 0; j < 8; j++) {
|
||||
offset = TEGRA186_GPIO_INT_ROUTE_MAPPING(p, j);
|
||||
|
||||
value = readl(base + offset);
|
||||
value = BIT(port->pins) - 1;
|
||||
writel(value, base + offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int tegra186_gpio_probe(struct platform_device *pdev)
|
||||
{
|
||||
unsigned int i, j, offset;
|
||||
struct gpio_irq_chip *irq;
|
||||
struct tegra_gpio *gpio;
|
||||
struct device_node *np;
|
||||
struct resource *res;
|
||||
char **names;
|
||||
int err;
|
||||
|
||||
|
@ -460,8 +541,11 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
|||
|
||||
gpio->soc = of_device_get_match_data(&pdev->dev);
|
||||
|
||||
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "gpio");
|
||||
gpio->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
gpio->secure = devm_platform_ioremap_resource_byname(pdev, "security");
|
||||
if (IS_ERR(gpio->secure))
|
||||
return PTR_ERR(gpio->secure);
|
||||
|
||||
gpio->base = devm_platform_ioremap_resource_byname(pdev, "gpio");
|
||||
if (IS_ERR(gpio->base))
|
||||
return PTR_ERR(gpio->base);
|
||||
|
||||
|
@ -492,6 +576,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
|||
gpio->gpio.direction_output = tegra186_gpio_direction_output;
|
||||
gpio->gpio.get = tegra186_gpio_get,
|
||||
gpio->gpio.set = tegra186_gpio_set;
|
||||
gpio->gpio.set_config = tegra186_gpio_set_config;
|
||||
|
||||
gpio->gpio.base = -1;
|
||||
|
||||
|
@ -555,6 +640,8 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
|||
return -EPROBE_DEFER;
|
||||
}
|
||||
|
||||
tegra186_gpio_init_route_mapping(gpio);
|
||||
|
||||
irq->map = devm_kcalloc(&pdev->dev, gpio->gpio.ngpio,
|
||||
sizeof(*irq->map), GFP_KERNEL);
|
||||
if (!irq->map)
|
||||
|
@ -564,7 +651,7 @@ static int tegra186_gpio_probe(struct platform_device *pdev)
|
|||
const struct tegra_gpio_port *port = &gpio->soc->ports[i];
|
||||
|
||||
for (j = 0; j < port->pins; j++)
|
||||
irq->map[offset + j] = irq->parents[port->irq];
|
||||
irq->map[offset + j] = irq->parents[port->bank];
|
||||
|
||||
offset += port->pins;
|
||||
}
|
||||
|
@ -583,38 +670,38 @@ static int tegra186_gpio_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#define TEGRA186_MAIN_GPIO_PORT(port, base, count, controller) \
|
||||
[TEGRA186_MAIN_GPIO_PORT_##port] = { \
|
||||
.name = #port, \
|
||||
.offset = base, \
|
||||
.pins = count, \
|
||||
.irq = controller, \
|
||||
#define TEGRA186_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
|
||||
[TEGRA186_MAIN_GPIO_PORT_##_name] = { \
|
||||
.name = #_name, \
|
||||
.bank = _bank, \
|
||||
.port = _port, \
|
||||
.pins = _pins, \
|
||||
}
|
||||
|
||||
static const struct tegra_gpio_port tegra186_main_ports[] = {
|
||||
TEGRA186_MAIN_GPIO_PORT( A, 0x2000, 7, 2),
|
||||
TEGRA186_MAIN_GPIO_PORT( B, 0x3000, 7, 3),
|
||||
TEGRA186_MAIN_GPIO_PORT( C, 0x3200, 7, 3),
|
||||
TEGRA186_MAIN_GPIO_PORT( D, 0x3400, 6, 3),
|
||||
TEGRA186_MAIN_GPIO_PORT( E, 0x2200, 8, 2),
|
||||
TEGRA186_MAIN_GPIO_PORT( F, 0x2400, 6, 2),
|
||||
TEGRA186_MAIN_GPIO_PORT( G, 0x4200, 6, 4),
|
||||
TEGRA186_MAIN_GPIO_PORT( H, 0x1000, 7, 1),
|
||||
TEGRA186_MAIN_GPIO_PORT( I, 0x0800, 8, 0),
|
||||
TEGRA186_MAIN_GPIO_PORT( J, 0x5000, 8, 5),
|
||||
TEGRA186_MAIN_GPIO_PORT( K, 0x5200, 1, 5),
|
||||
TEGRA186_MAIN_GPIO_PORT( L, 0x1200, 8, 1),
|
||||
TEGRA186_MAIN_GPIO_PORT( M, 0x5600, 6, 5),
|
||||
TEGRA186_MAIN_GPIO_PORT( N, 0x0000, 7, 0),
|
||||
TEGRA186_MAIN_GPIO_PORT( O, 0x0200, 4, 0),
|
||||
TEGRA186_MAIN_GPIO_PORT( P, 0x4000, 7, 4),
|
||||
TEGRA186_MAIN_GPIO_PORT( Q, 0x0400, 6, 0),
|
||||
TEGRA186_MAIN_GPIO_PORT( R, 0x0a00, 6, 0),
|
||||
TEGRA186_MAIN_GPIO_PORT( T, 0x0600, 4, 0),
|
||||
TEGRA186_MAIN_GPIO_PORT( X, 0x1400, 8, 1),
|
||||
TEGRA186_MAIN_GPIO_PORT( Y, 0x1600, 7, 1),
|
||||
TEGRA186_MAIN_GPIO_PORT(BB, 0x2600, 2, 2),
|
||||
TEGRA186_MAIN_GPIO_PORT(CC, 0x5400, 4, 5),
|
||||
TEGRA186_MAIN_GPIO_PORT( A, 2, 0, 7),
|
||||
TEGRA186_MAIN_GPIO_PORT( B, 3, 0, 7),
|
||||
TEGRA186_MAIN_GPIO_PORT( C, 3, 1, 7),
|
||||
TEGRA186_MAIN_GPIO_PORT( D, 3, 2, 6),
|
||||
TEGRA186_MAIN_GPIO_PORT( E, 2, 1, 8),
|
||||
TEGRA186_MAIN_GPIO_PORT( F, 2, 2, 6),
|
||||
TEGRA186_MAIN_GPIO_PORT( G, 4, 1, 6),
|
||||
TEGRA186_MAIN_GPIO_PORT( H, 1, 0, 7),
|
||||
TEGRA186_MAIN_GPIO_PORT( I, 0, 4, 8),
|
||||
TEGRA186_MAIN_GPIO_PORT( J, 5, 0, 8),
|
||||
TEGRA186_MAIN_GPIO_PORT( K, 5, 1, 1),
|
||||
TEGRA186_MAIN_GPIO_PORT( L, 1, 1, 8),
|
||||
TEGRA186_MAIN_GPIO_PORT( M, 5, 3, 6),
|
||||
TEGRA186_MAIN_GPIO_PORT( N, 0, 0, 7),
|
||||
TEGRA186_MAIN_GPIO_PORT( O, 0, 1, 4),
|
||||
TEGRA186_MAIN_GPIO_PORT( P, 4, 0, 7),
|
||||
TEGRA186_MAIN_GPIO_PORT( Q, 0, 2, 6),
|
||||
TEGRA186_MAIN_GPIO_PORT( R, 0, 5, 6),
|
||||
TEGRA186_MAIN_GPIO_PORT( T, 0, 3, 4),
|
||||
TEGRA186_MAIN_GPIO_PORT( X, 1, 2, 8),
|
||||
TEGRA186_MAIN_GPIO_PORT( Y, 1, 3, 7),
|
||||
TEGRA186_MAIN_GPIO_PORT(BB, 2, 3, 2),
|
||||
TEGRA186_MAIN_GPIO_PORT(CC, 5, 2, 4),
|
||||
};
|
||||
|
||||
static const struct tegra_gpio_soc tegra186_main_soc = {
|
||||
|
@ -624,23 +711,23 @@ static const struct tegra_gpio_soc tegra186_main_soc = {
|
|||
.instance = 0,
|
||||
};
|
||||
|
||||
#define TEGRA186_AON_GPIO_PORT(port, base, count, controller) \
|
||||
[TEGRA186_AON_GPIO_PORT_##port] = { \
|
||||
.name = #port, \
|
||||
.offset = base, \
|
||||
.pins = count, \
|
||||
.irq = controller, \
|
||||
#define TEGRA186_AON_GPIO_PORT(_name, _bank, _port, _pins) \
|
||||
[TEGRA186_AON_GPIO_PORT_##_name] = { \
|
||||
.name = #_name, \
|
||||
.bank = _bank, \
|
||||
.port = _port, \
|
||||
.pins = _pins, \
|
||||
}
|
||||
|
||||
static const struct tegra_gpio_port tegra186_aon_ports[] = {
|
||||
TEGRA186_AON_GPIO_PORT( S, 0x0200, 5, 0),
|
||||
TEGRA186_AON_GPIO_PORT( U, 0x0400, 6, 0),
|
||||
TEGRA186_AON_GPIO_PORT( V, 0x0800, 8, 0),
|
||||
TEGRA186_AON_GPIO_PORT( W, 0x0a00, 8, 0),
|
||||
TEGRA186_AON_GPIO_PORT( Z, 0x0e00, 4, 0),
|
||||
TEGRA186_AON_GPIO_PORT(AA, 0x0c00, 8, 0),
|
||||
TEGRA186_AON_GPIO_PORT(EE, 0x0600, 3, 0),
|
||||
TEGRA186_AON_GPIO_PORT(FF, 0x0000, 5, 0),
|
||||
TEGRA186_AON_GPIO_PORT( S, 0, 1, 5),
|
||||
TEGRA186_AON_GPIO_PORT( U, 0, 2, 6),
|
||||
TEGRA186_AON_GPIO_PORT( V, 0, 4, 8),
|
||||
TEGRA186_AON_GPIO_PORT( W, 0, 5, 8),
|
||||
TEGRA186_AON_GPIO_PORT( Z, 0, 7, 4),
|
||||
TEGRA186_AON_GPIO_PORT(AA, 0, 6, 8),
|
||||
TEGRA186_AON_GPIO_PORT(EE, 0, 3, 3),
|
||||
TEGRA186_AON_GPIO_PORT(FF, 0, 0, 5),
|
||||
};
|
||||
|
||||
static const struct tegra_gpio_soc tegra186_aon_soc = {
|
||||
|
@ -650,43 +737,43 @@ static const struct tegra_gpio_soc tegra186_aon_soc = {
|
|||
.instance = 1,
|
||||
};
|
||||
|
||||
#define TEGRA194_MAIN_GPIO_PORT(port, base, count, controller) \
|
||||
[TEGRA194_MAIN_GPIO_PORT_##port] = { \
|
||||
.name = #port, \
|
||||
.offset = base, \
|
||||
.pins = count, \
|
||||
.irq = controller, \
|
||||
#define TEGRA194_MAIN_GPIO_PORT(_name, _bank, _port, _pins) \
|
||||
[TEGRA194_MAIN_GPIO_PORT_##_name] = { \
|
||||
.name = #_name, \
|
||||
.bank = _bank, \
|
||||
.port = _port, \
|
||||
.pins = _pins, \
|
||||
}
|
||||
|
||||
static const struct tegra_gpio_port tegra194_main_ports[] = {
|
||||
TEGRA194_MAIN_GPIO_PORT( A, 0x1400, 8, 1),
|
||||
TEGRA194_MAIN_GPIO_PORT( B, 0x4e00, 2, 4),
|
||||
TEGRA194_MAIN_GPIO_PORT( C, 0x4600, 8, 4),
|
||||
TEGRA194_MAIN_GPIO_PORT( D, 0x4800, 4, 4),
|
||||
TEGRA194_MAIN_GPIO_PORT( E, 0x4a00, 8, 4),
|
||||
TEGRA194_MAIN_GPIO_PORT( F, 0x4c00, 6, 4),
|
||||
TEGRA194_MAIN_GPIO_PORT( G, 0x4000, 8, 4),
|
||||
TEGRA194_MAIN_GPIO_PORT( H, 0x4200, 8, 4),
|
||||
TEGRA194_MAIN_GPIO_PORT( I, 0x4400, 5, 4),
|
||||
TEGRA194_MAIN_GPIO_PORT( J, 0x5200, 6, 5),
|
||||
TEGRA194_MAIN_GPIO_PORT( K, 0x3000, 8, 3),
|
||||
TEGRA194_MAIN_GPIO_PORT( L, 0x3200, 4, 3),
|
||||
TEGRA194_MAIN_GPIO_PORT( M, 0x2600, 8, 2),
|
||||
TEGRA194_MAIN_GPIO_PORT( N, 0x2800, 3, 2),
|
||||
TEGRA194_MAIN_GPIO_PORT( O, 0x5000, 6, 5),
|
||||
TEGRA194_MAIN_GPIO_PORT( P, 0x2a00, 8, 2),
|
||||
TEGRA194_MAIN_GPIO_PORT( Q, 0x2c00, 8, 2),
|
||||
TEGRA194_MAIN_GPIO_PORT( R, 0x2e00, 6, 2),
|
||||
TEGRA194_MAIN_GPIO_PORT( S, 0x3600, 8, 3),
|
||||
TEGRA194_MAIN_GPIO_PORT( T, 0x3800, 8, 3),
|
||||
TEGRA194_MAIN_GPIO_PORT( U, 0x3a00, 1, 3),
|
||||
TEGRA194_MAIN_GPIO_PORT( V, 0x1000, 8, 1),
|
||||
TEGRA194_MAIN_GPIO_PORT( W, 0x1200, 2, 1),
|
||||
TEGRA194_MAIN_GPIO_PORT( X, 0x2000, 8, 2),
|
||||
TEGRA194_MAIN_GPIO_PORT( Y, 0x2200, 8, 2),
|
||||
TEGRA194_MAIN_GPIO_PORT( Z, 0x2400, 8, 2),
|
||||
TEGRA194_MAIN_GPIO_PORT(FF, 0x3400, 2, 3),
|
||||
TEGRA194_MAIN_GPIO_PORT(GG, 0x0000, 2, 0)
|
||||
TEGRA194_MAIN_GPIO_PORT( A, 1, 2, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( B, 4, 7, 2),
|
||||
TEGRA194_MAIN_GPIO_PORT( C, 4, 3, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( D, 4, 4, 4),
|
||||
TEGRA194_MAIN_GPIO_PORT( E, 4, 5, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( F, 4, 6, 6),
|
||||
TEGRA194_MAIN_GPIO_PORT( G, 4, 0, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( H, 4, 1, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( I, 4, 2, 5),
|
||||
TEGRA194_MAIN_GPIO_PORT( J, 5, 1, 6),
|
||||
TEGRA194_MAIN_GPIO_PORT( K, 3, 0, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( L, 3, 1, 4),
|
||||
TEGRA194_MAIN_GPIO_PORT( M, 2, 3, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( N, 2, 4, 3),
|
||||
TEGRA194_MAIN_GPIO_PORT( O, 5, 0, 6),
|
||||
TEGRA194_MAIN_GPIO_PORT( P, 2, 5, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( Q, 2, 6, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( R, 2, 7, 6),
|
||||
TEGRA194_MAIN_GPIO_PORT( S, 3, 3, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( T, 3, 4, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( U, 3, 5, 1),
|
||||
TEGRA194_MAIN_GPIO_PORT( V, 1, 0, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( W, 1, 1, 2),
|
||||
TEGRA194_MAIN_GPIO_PORT( X, 2, 0, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( Y, 2, 1, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT( Z, 2, 2, 8),
|
||||
TEGRA194_MAIN_GPIO_PORT(FF, 3, 2, 2),
|
||||
TEGRA194_MAIN_GPIO_PORT(GG, 0, 0, 2)
|
||||
};
|
||||
|
||||
static const struct tegra_gpio_soc tegra194_main_soc = {
|
||||
|
@ -696,20 +783,20 @@ static const struct tegra_gpio_soc tegra194_main_soc = {
|
|||
.instance = 0,
|
||||
};
|
||||
|
||||
#define TEGRA194_AON_GPIO_PORT(port, base, count, controller) \
|
||||
[TEGRA194_AON_GPIO_PORT_##port] = { \
|
||||
.name = #port, \
|
||||
.offset = base, \
|
||||
.pins = count, \
|
||||
.irq = controller, \
|
||||
#define TEGRA194_AON_GPIO_PORT(_name, _bank, _port, _pins) \
|
||||
[TEGRA194_AON_GPIO_PORT_##_name] = { \
|
||||
.name = #_name, \
|
||||
.bank = _bank, \
|
||||
.port = _port, \
|
||||
.pins = _pins, \
|
||||
}
|
||||
|
||||
static const struct tegra_gpio_port tegra194_aon_ports[] = {
|
||||
TEGRA194_AON_GPIO_PORT(AA, 0x0600, 8, 0),
|
||||
TEGRA194_AON_GPIO_PORT(BB, 0x0800, 4, 0),
|
||||
TEGRA194_AON_GPIO_PORT(CC, 0x0200, 8, 0),
|
||||
TEGRA194_AON_GPIO_PORT(DD, 0x0400, 3, 0),
|
||||
TEGRA194_AON_GPIO_PORT(EE, 0x0000, 7, 0)
|
||||
TEGRA194_AON_GPIO_PORT(AA, 0, 3, 8),
|
||||
TEGRA194_AON_GPIO_PORT(BB, 0, 4, 4),
|
||||
TEGRA194_AON_GPIO_PORT(CC, 0, 1, 8),
|
||||
TEGRA194_AON_GPIO_PORT(DD, 0, 2, 3),
|
||||
TEGRA194_AON_GPIO_PORT(EE, 0, 0, 7)
|
||||
};
|
||||
|
||||
static const struct tegra_gpio_soc tegra194_aon_soc = {
|
||||
|
|
|
@ -422,9 +422,127 @@ struct linehandle_state {
|
|||
(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 void linehandle_configure_flag(unsigned long *flagsp,
|
||||
u32 bit, bool active)
|
||||
{
|
||||
if (active)
|
||||
set_bit(bit, flagsp);
|
||||
else
|
||||
clear_bit(bit, flagsp);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
linehandle_configure_flag(flagsp, FLAG_ACTIVE_LOW,
|
||||
lflags & GPIOHANDLE_REQUEST_ACTIVE_LOW);
|
||||
|
||||
linehandle_configure_flag(flagsp, FLAG_OPEN_DRAIN,
|
||||
lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN);
|
||||
|
||||
linehandle_configure_flag(flagsp, FLAG_OPEN_SOURCE,
|
||||
lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE);
|
||||
|
||||
linehandle_configure_flag(flagsp, FLAG_PULL_UP,
|
||||
lflags & GPIOHANDLE_REQUEST_BIAS_PULL_UP);
|
||||
|
||||
linehandle_configure_flag(flagsp, FLAG_PULL_DOWN,
|
||||
lflags & GPIOHANDLE_REQUEST_BIAS_PULL_DOWN);
|
||||
|
||||
linehandle_configure_flag(flagsp, FLAG_BIAS_DISABLE,
|
||||
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;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static long linehandle_ioctl(struct file *filep, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
|
@ -475,6 +593,8 @@ static long linehandle_ioctl(struct file *filep, unsigned int cmd,
|
|||
lh->descs,
|
||||
NULL,
|
||||
vals);
|
||||
} else if (cmd == GPIOHANDLE_SET_CONFIG_IOCTL) {
|
||||
return linehandle_set_config(lh, ip);
|
||||
}
|
||||
return -EINVAL;
|
||||
}
|
||||
|
@ -526,32 +646,9 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
|
|||
|
||||
lflags = handlereq.flags;
|
||||
|
||||
/* Return an error if an unknown flag is set */
|
||||
if (lflags & ~GPIOHANDLE_REQUEST_VALID_FLAGS)
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Do not allow both INPUT & OUTPUT flags to be set as they are
|
||||
* contradictory.
|
||||
*/
|
||||
if ((lflags & GPIOHANDLE_REQUEST_INPUT) &&
|
||||
(lflags & 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 ((lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) &&
|
||||
(lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE))
|
||||
return -EINVAL;
|
||||
|
||||
/* OPEN_DRAIN and OPEN_SOURCE flags only make sense for output mode. */
|
||||
if (!(lflags & GPIOHANDLE_REQUEST_OUTPUT) &&
|
||||
((lflags & GPIOHANDLE_REQUEST_OPEN_DRAIN) ||
|
||||
(lflags & GPIOHANDLE_REQUEST_OPEN_SOURCE)))
|
||||
return -EINVAL;
|
||||
ret = linehandle_validate_flags(lflags);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
lh = kzalloc(sizeof(*lh), GFP_KERNEL);
|
||||
if (!lh)
|
||||
|
@ -593,6 +690,12 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
|
|||
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)
|
||||
|
@ -913,6 +1016,14 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
|
|||
(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;
|
||||
|
@ -939,6 +1050,12 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
|
|||
|
||||
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)
|
||||
|
@ -1092,6 +1209,12 @@ static long gpio_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
|
|||
if (test_bit(FLAG_OPEN_SOURCE, &desc->flags))
|
||||
lineinfo.flags |= (GPIOLINE_FLAG_OPEN_SOURCE |
|
||||
GPIOLINE_FLAG_IS_OUT);
|
||||
if (test_bit(FLAG_BIAS_DISABLE, &desc->flags))
|
||||
lineinfo.flags |= GPIOLINE_FLAG_BIAS_DISABLE;
|
||||
if (test_bit(FLAG_PULL_DOWN, &desc->flags))
|
||||
lineinfo.flags |= GPIOLINE_FLAG_BIAS_PULL_DOWN;
|
||||
if (test_bit(FLAG_PULL_UP, &desc->flags))
|
||||
lineinfo.flags |= GPIOLINE_FLAG_BIAS_PULL_UP;
|
||||
|
||||
if (copy_to_user(ip, &lineinfo, sizeof(lineinfo)))
|
||||
return -EFAULT;
|
||||
|
@ -2785,6 +2908,9 @@ static bool gpiod_free_commit(struct gpio_desc *desc)
|
|||
clear_bit(FLAG_REQUESTED, &desc->flags);
|
||||
clear_bit(FLAG_OPEN_DRAIN, &desc->flags);
|
||||
clear_bit(FLAG_OPEN_SOURCE, &desc->flags);
|
||||
clear_bit(FLAG_PULL_UP, &desc->flags);
|
||||
clear_bit(FLAG_PULL_DOWN, &desc->flags);
|
||||
clear_bit(FLAG_BIAS_DISABLE, &desc->flags);
|
||||
clear_bit(FLAG_IS_HOGGED, &desc->flags);
|
||||
ret = true;
|
||||
}
|
||||
|
@ -2911,6 +3037,7 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset,
|
|||
unsigned arg;
|
||||
|
||||
switch (mode) {
|
||||
case PIN_CONFIG_BIAS_DISABLE:
|
||||
case PIN_CONFIG_BIAS_PULL_DOWN:
|
||||
case PIN_CONFIG_BIAS_PULL_UP:
|
||||
arg = 1;
|
||||
|
@ -2924,6 +3051,26 @@ static int gpio_set_config(struct gpio_chip *gc, unsigned offset,
|
|||
return gc->set_config ? gc->set_config(gc, offset, config) : -ENOTSUPP;
|
||||
}
|
||||
|
||||
static int gpio_set_bias(struct gpio_chip *chip, struct gpio_desc *desc)
|
||||
{
|
||||
int bias = 0;
|
||||
int ret = 0;
|
||||
|
||||
if (test_bit(FLAG_BIAS_DISABLE, &desc->flags))
|
||||
bias = PIN_CONFIG_BIAS_DISABLE;
|
||||
else if (test_bit(FLAG_PULL_UP, &desc->flags))
|
||||
bias = PIN_CONFIG_BIAS_PULL_UP;
|
||||
else if (test_bit(FLAG_PULL_DOWN, &desc->flags))
|
||||
bias = PIN_CONFIG_BIAS_PULL_DOWN;
|
||||
|
||||
if (bias) {
|
||||
ret = gpio_set_config(chip, gpio_chip_hwgpio(desc), bias);
|
||||
if (ret != -ENOTSUPP)
|
||||
return ret;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* gpiod_direction_input - set the GPIO direction to input
|
||||
* @desc: GPIO to set to input
|
||||
|
@ -2968,15 +3115,10 @@ int gpiod_direction_input(struct gpio_desc *desc)
|
|||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
if (ret == 0)
|
||||
if (ret == 0) {
|
||||
clear_bit(FLAG_IS_OUT, &desc->flags);
|
||||
|
||||
if (test_bit(FLAG_PULL_UP, &desc->flags))
|
||||
gpio_set_config(chip, gpio_chip_hwgpio(desc),
|
||||
PIN_CONFIG_BIAS_PULL_UP);
|
||||
else if (test_bit(FLAG_PULL_DOWN, &desc->flags))
|
||||
gpio_set_config(chip, gpio_chip_hwgpio(desc),
|
||||
PIN_CONFIG_BIAS_PULL_DOWN);
|
||||
ret = gpio_set_bias(chip, desc);
|
||||
}
|
||||
|
||||
trace_gpio_direction(desc_to_gpio(desc), 1, ret);
|
||||
|
||||
|
@ -3106,6 +3248,9 @@ int gpiod_direction_output(struct gpio_desc *desc, int value)
|
|||
}
|
||||
|
||||
set_output_value:
|
||||
ret = gpio_set_bias(gc, desc);
|
||||
if (ret)
|
||||
return ret;
|
||||
return gpiod_direction_output_raw_commit(desc, value);
|
||||
|
||||
set_output_flag:
|
||||
|
|
|
@ -110,6 +110,7 @@ struct gpio_desc {
|
|||
#define FLAG_TRANSITORY 12 /* GPIO may lose value in sleep or reset */
|
||||
#define FLAG_PULL_UP 13 /* GPIO has pull up enabled */
|
||||
#define FLAG_PULL_DOWN 14 /* GPIO has pull down enabled */
|
||||
#define FLAG_BIAS_DISABLE 15 /* GPIO has pull disabled */
|
||||
|
||||
/* Connection label */
|
||||
const char *label;
|
||||
|
|
|
@ -5710,11 +5710,10 @@ static int mlx5_ib_rn_get_params(struct ib_device *device, u8 port_num,
|
|||
|
||||
static void delay_drop_debugfs_cleanup(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
if (!dev->delay_drop.dbg)
|
||||
if (!dev->delay_drop.dir_debugfs)
|
||||
return;
|
||||
debugfs_remove_recursive(dev->delay_drop.dbg->dir_debugfs);
|
||||
kfree(dev->delay_drop.dbg);
|
||||
dev->delay_drop.dbg = NULL;
|
||||
debugfs_remove_recursive(dev->delay_drop.dir_debugfs);
|
||||
dev->delay_drop.dir_debugfs = NULL;
|
||||
}
|
||||
|
||||
static void cancel_delay_drop(struct mlx5_ib_dev *dev)
|
||||
|
@ -5765,52 +5764,22 @@ static const struct file_operations fops_delay_drop_timeout = {
|
|||
.read = delay_drop_timeout_read,
|
||||
};
|
||||
|
||||
static int delay_drop_debugfs_init(struct mlx5_ib_dev *dev)
|
||||
static void delay_drop_debugfs_init(struct mlx5_ib_dev *dev)
|
||||
{
|
||||
struct mlx5_ib_dbg_delay_drop *dbg;
|
||||
struct dentry *root;
|
||||
|
||||
if (!mlx5_debugfs_root)
|
||||
return 0;
|
||||
return;
|
||||
|
||||
dbg = kzalloc(sizeof(*dbg), GFP_KERNEL);
|
||||
if (!dbg)
|
||||
return -ENOMEM;
|
||||
root = debugfs_create_dir("delay_drop", dev->mdev->priv.dbg_root);
|
||||
dev->delay_drop.dir_debugfs = root;
|
||||
|
||||
dev->delay_drop.dbg = dbg;
|
||||
|
||||
dbg->dir_debugfs =
|
||||
debugfs_create_dir("delay_drop",
|
||||
dev->mdev->priv.dbg_root);
|
||||
if (!dbg->dir_debugfs)
|
||||
goto out_debugfs;
|
||||
|
||||
dbg->events_cnt_debugfs =
|
||||
debugfs_create_atomic_t("num_timeout_events", 0400,
|
||||
dbg->dir_debugfs,
|
||||
&dev->delay_drop.events_cnt);
|
||||
if (!dbg->events_cnt_debugfs)
|
||||
goto out_debugfs;
|
||||
|
||||
dbg->rqs_cnt_debugfs =
|
||||
debugfs_create_atomic_t("num_rqs", 0400,
|
||||
dbg->dir_debugfs,
|
||||
&dev->delay_drop.rqs_cnt);
|
||||
if (!dbg->rqs_cnt_debugfs)
|
||||
goto out_debugfs;
|
||||
|
||||
dbg->timeout_debugfs =
|
||||
debugfs_create_file("timeout", 0600,
|
||||
dbg->dir_debugfs,
|
||||
&dev->delay_drop,
|
||||
&fops_delay_drop_timeout);
|
||||
if (!dbg->timeout_debugfs)
|
||||
goto out_debugfs;
|
||||
|
||||
return 0;
|
||||
|
||||
out_debugfs:
|
||||
delay_drop_debugfs_cleanup(dev);
|
||||
return -ENOMEM;
|
||||
debugfs_create_atomic_t("num_timeout_events", 0400, root,
|
||||
&dev->delay_drop.events_cnt);
|
||||
debugfs_create_atomic_t("num_rqs", 0400, root,
|
||||
&dev->delay_drop.rqs_cnt);
|
||||
debugfs_create_file("timeout", 0600, root, &dev->delay_drop,
|
||||
&fops_delay_drop_timeout);
|
||||
}
|
||||
|
||||
static void init_delay_drop(struct mlx5_ib_dev *dev)
|
||||
|
@ -5826,8 +5795,7 @@ static void init_delay_drop(struct mlx5_ib_dev *dev)
|
|||
atomic_set(&dev->delay_drop.rqs_cnt, 0);
|
||||
atomic_set(&dev->delay_drop.events_cnt, 0);
|
||||
|
||||
if (delay_drop_debugfs_init(dev))
|
||||
mlx5_ib_warn(dev, "Failed to init delay drop debugfs\n");
|
||||
delay_drop_debugfs_init(dev);
|
||||
}
|
||||
|
||||
static void mlx5_ib_unbind_slave_port(struct mlx5_ib_dev *ibdev,
|
||||
|
|
|
@ -792,13 +792,6 @@ enum {
|
|||
MLX5_MAX_DELAY_DROP_TIMEOUT_MS = 100,
|
||||
};
|
||||
|
||||
struct mlx5_ib_dbg_delay_drop {
|
||||
struct dentry *dir_debugfs;
|
||||
struct dentry *rqs_cnt_debugfs;
|
||||
struct dentry *events_cnt_debugfs;
|
||||
struct dentry *timeout_debugfs;
|
||||
};
|
||||
|
||||
struct mlx5_ib_delay_drop {
|
||||
struct mlx5_ib_dev *dev;
|
||||
struct work_struct delay_drop_work;
|
||||
|
@ -808,7 +801,7 @@ struct mlx5_ib_delay_drop {
|
|||
bool activate;
|
||||
atomic_t events_cnt;
|
||||
atomic_t rqs_cnt;
|
||||
struct mlx5_ib_dbg_delay_drop *dbg;
|
||||
struct dentry *dir_debugfs;
|
||||
};
|
||||
|
||||
enum mlx5_ib_stages {
|
||||
|
|
|
@ -225,36 +225,16 @@ static const struct debugfs_reg32 fei_sys_regs[] = {
|
|||
|
||||
void c8sectpfe_debugfs_init(struct c8sectpfei *fei)
|
||||
{
|
||||
struct dentry *root;
|
||||
struct dentry *file;
|
||||
|
||||
root = debugfs_create_dir("c8sectpfe", NULL);
|
||||
if (!root)
|
||||
goto err;
|
||||
|
||||
fei->root = root;
|
||||
|
||||
fei->regset = devm_kzalloc(fei->dev, sizeof(*fei->regset), GFP_KERNEL);
|
||||
if (!fei->regset)
|
||||
goto err;
|
||||
return;
|
||||
|
||||
fei->regset->regs = fei_sys_regs;
|
||||
fei->regset->nregs = ARRAY_SIZE(fei_sys_regs);
|
||||
fei->regset->base = fei->io;
|
||||
|
||||
file = debugfs_create_regset32("registers", S_IRUGO, root,
|
||||
fei->regset);
|
||||
if (!file) {
|
||||
dev_err(fei->dev,
|
||||
"%s not able to create 'registers' debugfs\n"
|
||||
, __func__);
|
||||
goto err;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
err:
|
||||
debugfs_remove_recursive(root);
|
||||
fei->root = debugfs_create_dir("c8sectpfe", NULL);
|
||||
debugfs_create_regset32("registers", S_IRUGO, fei->root, fei->regset);
|
||||
}
|
||||
|
||||
void c8sectpfe_debugfs_exit(struct c8sectpfei *fei)
|
||||
|
|
|
@ -340,8 +340,6 @@ static const struct of_device_id sram_dt_ids[] = {
|
|||
static int sram_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sram_dev *sram;
|
||||
struct resource *res;
|
||||
size_t size;
|
||||
int ret;
|
||||
int (*init_func)(void);
|
||||
|
||||
|
@ -351,25 +349,14 @@ static int sram_probe(struct platform_device *pdev)
|
|||
|
||||
sram->dev = &pdev->dev;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res) {
|
||||
dev_err(sram->dev, "found no memory resource\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
size = resource_size(res);
|
||||
|
||||
if (!devm_request_mem_region(sram->dev, res->start, size, pdev->name)) {
|
||||
dev_err(sram->dev, "could not request region for resource\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
if (of_property_read_bool(pdev->dev.of_node, "no-memory-wc"))
|
||||
sram->virt_base = devm_ioremap(sram->dev, res->start, size);
|
||||
sram->virt_base = devm_platform_ioremap_resource(pdev, 0);
|
||||
else
|
||||
sram->virt_base = devm_ioremap_wc(sram->dev, res->start, size);
|
||||
if (!sram->virt_base)
|
||||
return -ENOMEM;
|
||||
sram->virt_base = devm_platform_ioremap_resource_wc(pdev, 0);
|
||||
if (IS_ERR(sram->virt_base)) {
|
||||
dev_err(&pdev->dev, "could not map SRAM registers\n");
|
||||
return PTR_ERR(sram->virt_base);
|
||||
}
|
||||
|
||||
sram->pool = devm_gen_pool_create(sram->dev, ilog2(SRAM_GRANULARITY),
|
||||
NUMA_NO_NODE, NULL);
|
||||
|
@ -382,7 +369,8 @@ static int sram_probe(struct platform_device *pdev)
|
|||
else
|
||||
clk_prepare_enable(sram->clk);
|
||||
|
||||
ret = sram_reserve_regions(sram, res);
|
||||
ret = sram_reserve_regions(sram,
|
||||
platform_get_resource(pdev, IORESOURCE_MEM, 0));
|
||||
if (ret)
|
||||
goto err_disable_clk;
|
||||
|
||||
|
|
|
@ -583,11 +583,11 @@ static void atmci_init_debugfs(struct atmel_mci_slot *slot)
|
|||
|
||||
debugfs_create_file("regs", S_IRUSR, root, host, &atmci_regs_fops);
|
||||
debugfs_create_file("req", S_IRUSR, root, slot, &atmci_req_fops);
|
||||
debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
|
||||
debugfs_create_x32("pending_events", S_IRUSR, root,
|
||||
(u32 *)&host->pending_events);
|
||||
debugfs_create_x32("completed_events", S_IRUSR, root,
|
||||
(u32 *)&host->completed_events);
|
||||
debugfs_create_u32("state", S_IRUSR, root, &host->state);
|
||||
debugfs_create_xul("pending_events", S_IRUSR, root,
|
||||
&host->pending_events);
|
||||
debugfs_create_xul("completed_events", S_IRUSR, root,
|
||||
&host->completed_events);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_OF)
|
||||
|
|
|
@ -176,11 +176,11 @@ static void dw_mci_init_debugfs(struct dw_mci_slot *slot)
|
|||
|
||||
debugfs_create_file("regs", S_IRUSR, root, host, &dw_mci_regs_fops);
|
||||
debugfs_create_file("req", S_IRUSR, root, slot, &dw_mci_req_fops);
|
||||
debugfs_create_u32("state", S_IRUSR, root, (u32 *)&host->state);
|
||||
debugfs_create_x32("pending_events", S_IRUSR, root,
|
||||
(u32 *)&host->pending_events);
|
||||
debugfs_create_x32("completed_events", S_IRUSR, root,
|
||||
(u32 *)&host->completed_events);
|
||||
debugfs_create_u32("state", S_IRUSR, root, &host->state);
|
||||
debugfs_create_xul("pending_events", S_IRUSR, root,
|
||||
&host->pending_events);
|
||||
debugfs_create_xul("completed_events", S_IRUSR, root,
|
||||
&host->completed_events);
|
||||
}
|
||||
#endif /* defined(CONFIG_DEBUG_FS) */
|
||||
|
||||
|
|
|
@ -102,8 +102,8 @@ static inline void debugfs_init(struct ser_device *ser, struct tty_struct *tty)
|
|||
debugfs_create_blob("last_rx_msg", 0400, ser->debugfs_tty_dir,
|
||||
&ser->rx_blob);
|
||||
|
||||
debugfs_create_x32("ser_state", 0400, ser->debugfs_tty_dir,
|
||||
(u32 *)&ser->state);
|
||||
debugfs_create_xul("ser_state", 0400, ser->debugfs_tty_dir,
|
||||
&ser->state);
|
||||
|
||||
debugfs_create_x8("tty_status", 0400, ser->debugfs_tty_dir,
|
||||
&ser->tty_status);
|
||||
|
|
|
@ -354,13 +354,10 @@ static void pp_clear_ctx(struct pp_ctx *pp)
|
|||
static void pp_setup_dbgfs(struct pp_ctx *pp)
|
||||
{
|
||||
struct pci_dev *pdev = pp->ntb->pdev;
|
||||
void *ret;
|
||||
|
||||
pp->dbgfs_dir = debugfs_create_dir(pci_name(pdev), pp_dbgfs_topdir);
|
||||
|
||||
ret = debugfs_create_atomic_t("count", 0600, pp->dbgfs_dir, &pp->count);
|
||||
if (!ret)
|
||||
dev_warn(&pp->ntb->dev, "DebugFS unsupported\n");
|
||||
debugfs_create_atomic_t("count", 0600, pp->dbgfs_dir, &pp->count);
|
||||
}
|
||||
|
||||
static void pp_clear_dbgfs(struct pp_ctx *pp)
|
||||
|
|
|
@ -480,6 +480,7 @@ int of_platform_populate(struct device_node *root,
|
|||
pr_debug("%s()\n", __func__);
|
||||
pr_debug(" starting at: %pOF\n", root);
|
||||
|
||||
device_links_supplier_sync_state_pause();
|
||||
for_each_child_of_node(root, child) {
|
||||
rc = of_platform_bus_create(child, matches, lookup, parent, true);
|
||||
if (rc) {
|
||||
|
@ -487,6 +488,8 @@ int of_platform_populate(struct device_node *root,
|
|||
break;
|
||||
}
|
||||
}
|
||||
device_links_supplier_sync_state_resume();
|
||||
|
||||
of_node_set_flag(root, OF_POPULATED_BUS);
|
||||
|
||||
of_node_put(root);
|
||||
|
@ -518,6 +521,7 @@ static int __init of_platform_default_populate_init(void)
|
|||
if (!of_have_populated_dt())
|
||||
return -ENODEV;
|
||||
|
||||
device_links_supplier_sync_state_pause();
|
||||
/*
|
||||
* Handle certain compatibles explicitly, since we don't want to create
|
||||
* platform_devices for every node in /reserved-memory with a
|
||||
|
@ -538,6 +542,14 @@ static int __init of_platform_default_populate_init(void)
|
|||
return 0;
|
||||
}
|
||||
arch_initcall_sync(of_platform_default_populate_init);
|
||||
|
||||
static int __init of_platform_sync_state_init(void)
|
||||
{
|
||||
if (of_have_populated_dt())
|
||||
device_links_supplier_sync_state_resume();
|
||||
return 0;
|
||||
}
|
||||
late_initcall_sync(of_platform_sync_state_init);
|
||||
#endif
|
||||
|
||||
int of_platform_device_destroy(struct device *dev, void *data)
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <linux/of_device.h>
|
||||
#include <linux/of_graph.h>
|
||||
#include <linux/string.h>
|
||||
#include <linux/moduleparam.h>
|
||||
|
||||
#include "of_private.h"
|
||||
|
||||
|
@ -985,6 +986,302 @@ of_fwnode_device_get_match_data(const struct fwnode_handle *fwnode,
|
|||
return of_device_get_match_data(dev);
|
||||
}
|
||||
|
||||
static bool of_is_ancestor_of(struct device_node *test_ancestor,
|
||||
struct device_node *child)
|
||||
{
|
||||
of_node_get(child);
|
||||
while (child) {
|
||||
if (child == test_ancestor) {
|
||||
of_node_put(child);
|
||||
return false;
|
||||
}
|
||||
child = of_get_next_parent(child);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* of_link_to_phandle - Add device link to supplier from supplier phandle
|
||||
* @dev: consumer device
|
||||
* @sup_np: phandle to supplier device tree node
|
||||
*
|
||||
* Given a phandle to a supplier device tree node (@sup_np), this function
|
||||
* finds the device that owns the supplier device tree node and creates a
|
||||
* device link from @dev consumer device to the supplier device. This function
|
||||
* doesn't create device links for invalid scenarios such as trying to create a
|
||||
* link with a parent device as the consumer of its child device. In such
|
||||
* cases, it returns an error.
|
||||
*
|
||||
* Returns:
|
||||
* - 0 if link successfully created to supplier
|
||||
* - -EAGAIN if linking to the supplier should be reattempted
|
||||
* - -EINVAL if the supplier link is invalid and should not be created
|
||||
* - -ENODEV if there is no device that corresponds to the supplier phandle
|
||||
*/
|
||||
static int of_link_to_phandle(struct device *dev, struct device_node *sup_np,
|
||||
u32 dl_flags)
|
||||
{
|
||||
struct device *sup_dev;
|
||||
int ret = 0;
|
||||
struct device_node *tmp_np = sup_np;
|
||||
int is_populated;
|
||||
|
||||
of_node_get(sup_np);
|
||||
/*
|
||||
* Find the device node that contains the supplier phandle. It may be
|
||||
* @sup_np or it may be an ancestor of @sup_np.
|
||||
*/
|
||||
while (sup_np && !of_find_property(sup_np, "compatible", NULL))
|
||||
sup_np = of_get_next_parent(sup_np);
|
||||
if (!sup_np) {
|
||||
dev_dbg(dev, "Not linking to %pOFP - No device\n", tmp_np);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
/*
|
||||
* Don't allow linking a device node as a consumer of one of its
|
||||
* descendant nodes. By definition, a child node can't be a functional
|
||||
* dependency for the parent node.
|
||||
*/
|
||||
if (!of_is_ancestor_of(dev->of_node, sup_np)) {
|
||||
dev_dbg(dev, "Not linking to %pOFP - is descendant\n", sup_np);
|
||||
of_node_put(sup_np);
|
||||
return -EINVAL;
|
||||
}
|
||||
sup_dev = get_dev_from_fwnode(&sup_np->fwnode);
|
||||
is_populated = of_node_check_flag(sup_np, OF_POPULATED);
|
||||
of_node_put(sup_np);
|
||||
if (!sup_dev && is_populated) {
|
||||
/* Early device without struct device. */
|
||||
dev_dbg(dev, "Not linking to %pOFP - No struct device\n",
|
||||
sup_np);
|
||||
return -ENODEV;
|
||||
} else if (!sup_dev) {
|
||||
return -EAGAIN;
|
||||
}
|
||||
if (!device_link_add(dev, sup_dev, dl_flags))
|
||||
ret = -EAGAIN;
|
||||
put_device(sup_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* parse_prop_cells - Property parsing function for suppliers
|
||||
*
|
||||
* @np: Pointer to device tree node containing a list
|
||||
* @prop_name: Name of property to be parsed. Expected to hold phandle values
|
||||
* @index: For properties holding a list of phandles, this is the index
|
||||
* into the list.
|
||||
* @list_name: Property name that is known to contain list of phandle(s) to
|
||||
* supplier(s)
|
||||
* @cells_name: property name that specifies phandles' arguments count
|
||||
*
|
||||
* This is a helper function to parse properties that have a known fixed name
|
||||
* and are a list of phandles and phandle arguments.
|
||||
*
|
||||
* Returns:
|
||||
* - phandle node pointer with refcount incremented. Caller must of_node_put()
|
||||
* on it when done.
|
||||
* - NULL if no phandle found at index
|
||||
*/
|
||||
static struct device_node *parse_prop_cells(struct device_node *np,
|
||||
const char *prop_name, int index,
|
||||
const char *list_name,
|
||||
const char *cells_name)
|
||||
{
|
||||
struct of_phandle_args sup_args;
|
||||
|
||||
if (strcmp(prop_name, list_name))
|
||||
return NULL;
|
||||
|
||||
if (of_parse_phandle_with_args(np, list_name, cells_name, index,
|
||||
&sup_args))
|
||||
return NULL;
|
||||
|
||||
return sup_args.np;
|
||||
}
|
||||
|
||||
#define DEFINE_SIMPLE_PROP(fname, name, cells) \
|
||||
static struct device_node *parse_##fname(struct device_node *np, \
|
||||
const char *prop_name, int index) \
|
||||
{ \
|
||||
return parse_prop_cells(np, prop_name, index, name, cells); \
|
||||
}
|
||||
|
||||
static int strcmp_suffix(const char *str, const char *suffix)
|
||||
{
|
||||
unsigned int len, suffix_len;
|
||||
|
||||
len = strlen(str);
|
||||
suffix_len = strlen(suffix);
|
||||
if (len <= suffix_len)
|
||||
return -1;
|
||||
return strcmp(str + len - suffix_len, suffix);
|
||||
}
|
||||
|
||||
/**
|
||||
* parse_suffix_prop_cells - Suffix property parsing function for suppliers
|
||||
*
|
||||
* @np: Pointer to device tree node containing a list
|
||||
* @prop_name: Name of property to be parsed. Expected to hold phandle values
|
||||
* @index: For properties holding a list of phandles, this is the index
|
||||
* into the list.
|
||||
* @suffix: Property suffix that is known to contain list of phandle(s) to
|
||||
* supplier(s)
|
||||
* @cells_name: property name that specifies phandles' arguments count
|
||||
*
|
||||
* This is a helper function to parse properties that have a known fixed suffix
|
||||
* and are a list of phandles and phandle arguments.
|
||||
*
|
||||
* Returns:
|
||||
* - phandle node pointer with refcount incremented. Caller must of_node_put()
|
||||
* on it when done.
|
||||
* - NULL if no phandle found at index
|
||||
*/
|
||||
static struct device_node *parse_suffix_prop_cells(struct device_node *np,
|
||||
const char *prop_name, int index,
|
||||
const char *suffix,
|
||||
const char *cells_name)
|
||||
{
|
||||
struct of_phandle_args sup_args;
|
||||
|
||||
if (strcmp_suffix(prop_name, suffix))
|
||||
return NULL;
|
||||
|
||||
if (of_parse_phandle_with_args(np, prop_name, cells_name, index,
|
||||
&sup_args))
|
||||
return NULL;
|
||||
|
||||
return sup_args.np;
|
||||
}
|
||||
|
||||
#define DEFINE_SUFFIX_PROP(fname, suffix, cells) \
|
||||
static struct device_node *parse_##fname(struct device_node *np, \
|
||||
const char *prop_name, int index) \
|
||||
{ \
|
||||
return parse_suffix_prop_cells(np, prop_name, index, suffix, cells); \
|
||||
}
|
||||
|
||||
/**
|
||||
* struct supplier_bindings - Property parsing functions for suppliers
|
||||
*
|
||||
* @parse_prop: function name
|
||||
* parse_prop() finds the node corresponding to a supplier phandle
|
||||
* @parse_prop.np: Pointer to device node holding supplier phandle property
|
||||
* @parse_prop.prop_name: Name of property holding a phandle value
|
||||
* @parse_prop.index: For properties holding a list of phandles, this is the
|
||||
* index into the list
|
||||
*
|
||||
* Returns:
|
||||
* parse_prop() return values are
|
||||
* - phandle node pointer with refcount incremented. Caller must of_node_put()
|
||||
* on it when done.
|
||||
* - NULL if no phandle found at index
|
||||
*/
|
||||
struct supplier_bindings {
|
||||
struct device_node *(*parse_prop)(struct device_node *np,
|
||||
const char *prop_name, int index);
|
||||
};
|
||||
|
||||
DEFINE_SIMPLE_PROP(clocks, "clocks", "#clock-cells")
|
||||
DEFINE_SIMPLE_PROP(interconnects, "interconnects", "#interconnect-cells")
|
||||
DEFINE_SIMPLE_PROP(iommus, "iommus", "#iommu-cells")
|
||||
DEFINE_SIMPLE_PROP(mboxes, "mboxes", "#mbox-cells")
|
||||
DEFINE_SIMPLE_PROP(io_channels, "io-channel", "#io-channel-cells")
|
||||
DEFINE_SUFFIX_PROP(regulators, "-supply", NULL)
|
||||
|
||||
static const struct supplier_bindings of_supplier_bindings[] = {
|
||||
{ .parse_prop = parse_clocks, },
|
||||
{ .parse_prop = parse_interconnects, },
|
||||
{ .parse_prop = parse_iommus, },
|
||||
{ .parse_prop = parse_mboxes, },
|
||||
{ .parse_prop = parse_io_channels, },
|
||||
{ .parse_prop = parse_regulators, },
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* of_link_property - Create device links to suppliers listed in a property
|
||||
* @dev: Consumer device
|
||||
* @con_np: The consumer device tree node which contains the property
|
||||
* @prop_name: Name of property to be parsed
|
||||
*
|
||||
* This function checks if the property @prop_name that is present in the
|
||||
* @con_np device tree node is one of the known common device tree bindings
|
||||
* that list phandles to suppliers. If @prop_name isn't one, this function
|
||||
* doesn't do anything.
|
||||
*
|
||||
* If @prop_name is one, this function attempts to create device links from the
|
||||
* consumer device @dev to all the devices of the suppliers listed in
|
||||
* @prop_name.
|
||||
*
|
||||
* Any failed attempt to create a device link will NOT result in an immediate
|
||||
* return. of_link_property() must create links to all the available supplier
|
||||
* devices even when attempts to create a link to one or more suppliers fail.
|
||||
*/
|
||||
static int of_link_property(struct device *dev, struct device_node *con_np,
|
||||
const char *prop_name)
|
||||
{
|
||||
struct device_node *phandle;
|
||||
const struct supplier_bindings *s = of_supplier_bindings;
|
||||
unsigned int i = 0;
|
||||
bool matched = false;
|
||||
int ret = 0;
|
||||
u32 dl_flags;
|
||||
|
||||
if (dev->of_node == con_np)
|
||||
dl_flags = DL_FLAG_AUTOPROBE_CONSUMER;
|
||||
else
|
||||
dl_flags = DL_FLAG_SYNC_STATE_ONLY;
|
||||
|
||||
/* Do not stop at first failed link, link all available suppliers. */
|
||||
while (!matched && s->parse_prop) {
|
||||
while ((phandle = s->parse_prop(con_np, prop_name, i))) {
|
||||
matched = true;
|
||||
i++;
|
||||
if (of_link_to_phandle(dev, phandle, dl_flags)
|
||||
== -EAGAIN)
|
||||
ret = -EAGAIN;
|
||||
of_node_put(phandle);
|
||||
}
|
||||
s++;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int of_link_to_suppliers(struct device *dev,
|
||||
struct device_node *con_np)
|
||||
{
|
||||
struct device_node *child;
|
||||
struct property *p;
|
||||
int ret = 0;
|
||||
|
||||
for_each_property_of_node(con_np, p)
|
||||
if (of_link_property(dev, con_np, p->name))
|
||||
ret = -ENODEV;
|
||||
|
||||
for_each_child_of_node(con_np, child)
|
||||
if (of_link_to_suppliers(dev, child) && !ret)
|
||||
ret = -EAGAIN;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static bool of_devlink;
|
||||
core_param(of_devlink, of_devlink, bool, 0);
|
||||
|
||||
static int of_fwnode_add_links(const struct fwnode_handle *fwnode,
|
||||
struct device *dev)
|
||||
{
|
||||
if (!of_devlink)
|
||||
return 0;
|
||||
|
||||
if (unlikely(!is_of_node(fwnode)))
|
||||
return 0;
|
||||
|
||||
return of_link_to_suppliers(dev, to_of_node(fwnode));
|
||||
}
|
||||
|
||||
const struct fwnode_operations of_fwnode_ops = {
|
||||
.get = of_fwnode_get,
|
||||
.put = of_fwnode_put,
|
||||
|
@ -1001,5 +1298,6 @@ const struct fwnode_operations of_fwnode_ops = {
|
|||
.graph_get_remote_endpoint = of_fwnode_graph_get_remote_endpoint,
|
||||
.graph_get_port_parent = of_fwnode_graph_get_port_parent,
|
||||
.graph_parse_endpoint = of_fwnode_graph_parse_endpoint,
|
||||
.add_links = of_fwnode_add_links,
|
||||
};
|
||||
EXPORT_SYMBOL_GPL(of_fwnode_ops);
|
||||
|
|
|
@ -54,6 +54,7 @@
|
|||
|
||||
#ifdef CONFIG_SUPERH
|
||||
#include <asm/sh_bios.h>
|
||||
#include <asm/platform_early.h>
|
||||
#endif
|
||||
|
||||
#include "serial_mctrl_gpio.h"
|
||||
|
@ -3090,6 +3091,7 @@ static struct console serial_console = {
|
|||
.data = &sci_uart_driver,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_SUPERH
|
||||
static struct console early_serial_console = {
|
||||
.name = "early_ttySC",
|
||||
.write = serial_console_write,
|
||||
|
@ -3118,6 +3120,7 @@ static int sci_probe_earlyprintk(struct platform_device *pdev)
|
|||
register_console(&early_serial_console);
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#define SCI_CONSOLE (&serial_console)
|
||||
|
||||
|
@ -3318,8 +3321,10 @@ static int sci_probe(struct platform_device *dev)
|
|||
* the special early probe. We don't have sufficient device state
|
||||
* to make it beyond this yet.
|
||||
*/
|
||||
if (is_early_platform_device(dev))
|
||||
#ifdef CONFIG_SUPERH
|
||||
if (is_sh_early_platform_device(dev))
|
||||
return sci_probe_earlyprintk(dev);
|
||||
#endif
|
||||
|
||||
if (dev->dev.of_node) {
|
||||
p = sci_parse_dt(dev, &dev_id);
|
||||
|
@ -3414,8 +3419,8 @@ static void __exit sci_exit(void)
|
|||
uart_unregister_driver(&sci_uart_driver);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_SERIAL_SH_SCI_CONSOLE
|
||||
early_platform_init_buffer("earlyprintk", &sci_driver,
|
||||
#if defined(CONFIG_SUPERH) && defined(CONFIG_SERIAL_SH_SCI_CONSOLE)
|
||||
sh_early_platform_init_buffer("earlyprintk", &sci_driver,
|
||||
early_serial_buf, ARRAY_SIZE(early_serial_buf));
|
||||
#endif
|
||||
#ifdef CONFIG_SERIAL_SH_SCI_EARLYCON
|
||||
|
|
|
@ -420,20 +420,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_u8_wo, NULL, debugfs_u8_set, "%llu\n");
|
|||
* This function creates a file in debugfs with the given name that
|
||||
* contains the value of the variable @value. If the @mode variable is so
|
||||
* set, it can be read from, and written to.
|
||||
*
|
||||
* This function will return a pointer to a dentry if it succeeds. This
|
||||
* pointer must be passed to the debugfs_remove() function when the file is
|
||||
* to be removed (no automatic cleanup happens if your module is unloaded,
|
||||
* you are responsible here.) If an error occurs, %ERR_PTR(-ERROR) will be
|
||||
* returned.
|
||||
*
|
||||
* If debugfs is not enabled in the kernel, the value %ERR_PTR(-ENODEV) will
|
||||
* be returned.
|
||||
*/
|
||||
struct dentry *debugfs_create_u8(const char *name, umode_t mode,
|
||||
struct dentry *parent, u8 *value)
|
||||
void debugfs_create_u8(const char *name, umode_t mode, struct dentry *parent,
|
||||
u8 *value)
|
||||
{
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u8,
|
||||
debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u8,
|
||||
&fops_u8_ro, &fops_u8_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_u8);
|
||||
|
@ -465,20 +456,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_u16_wo, NULL, debugfs_u16_set, "%llu\n");
|
|||
* This function creates a file in debugfs with the given name that
|
||||
* contains the value of the variable @value. If the @mode variable is so
|
||||
* set, it can be read from, and written to.
|
||||
*
|
||||
* This function will return a pointer to a dentry if it succeeds. This
|
||||
* pointer must be passed to the debugfs_remove() function when the file is
|
||||
* to be removed (no automatic cleanup happens if your module is unloaded,
|
||||
* you are responsible here.) If an error occurs, %ERR_PTR(-ERROR) will be
|
||||
* returned.
|
||||
*
|
||||
* If debugfs is not enabled in the kernel, the value %ERR_PTR(-ENODEV) will
|
||||
* be returned.
|
||||
*/
|
||||
struct dentry *debugfs_create_u16(const char *name, umode_t mode,
|
||||
struct dentry *parent, u16 *value)
|
||||
void debugfs_create_u16(const char *name, umode_t mode, struct dentry *parent,
|
||||
u16 *value)
|
||||
{
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u16,
|
||||
debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u16,
|
||||
&fops_u16_ro, &fops_u16_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_u16);
|
||||
|
@ -556,20 +538,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_u64_wo, NULL, debugfs_u64_set, "%llu\n");
|
|||
* This function creates a file in debugfs with the given name that
|
||||
* contains the value of the variable @value. If the @mode variable is so
|
||||
* set, it can be read from, and written to.
|
||||
*
|
||||
* This function will return a pointer to a dentry if it succeeds. This
|
||||
* pointer must be passed to the debugfs_remove() function when the file is
|
||||
* to be removed (no automatic cleanup happens if your module is unloaded,
|
||||
* you are responsible here.) If an error occurs, %ERR_PTR(-ERROR) will be
|
||||
* returned.
|
||||
*
|
||||
* If debugfs is not enabled in the kernel, the value %ERR_PTR(-ENODEV) will
|
||||
* be returned.
|
||||
*/
|
||||
struct dentry *debugfs_create_u64(const char *name, umode_t mode,
|
||||
struct dentry *parent, u64 *value)
|
||||
void debugfs_create_u64(const char *name, umode_t mode, struct dentry *parent,
|
||||
u64 *value)
|
||||
{
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u64,
|
||||
debugfs_create_mode_unsafe(name, mode, parent, value, &fops_u64,
|
||||
&fops_u64_ro, &fops_u64_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_u64);
|
||||
|
@ -660,10 +633,10 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_x64_wo, NULL, debugfs_u64_set, "0x%016llx\n");
|
|||
* @value: a pointer to the variable that the file should read to and write
|
||||
* from.
|
||||
*/
|
||||
struct dentry *debugfs_create_x8(const char *name, umode_t mode,
|
||||
struct dentry *parent, u8 *value)
|
||||
void debugfs_create_x8(const char *name, umode_t mode, struct dentry *parent,
|
||||
u8 *value)
|
||||
{
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x8,
|
||||
debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x8,
|
||||
&fops_x8_ro, &fops_x8_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_x8);
|
||||
|
@ -678,10 +651,10 @@ EXPORT_SYMBOL_GPL(debugfs_create_x8);
|
|||
* @value: a pointer to the variable that the file should read to and write
|
||||
* from.
|
||||
*/
|
||||
struct dentry *debugfs_create_x16(const char *name, umode_t mode,
|
||||
struct dentry *parent, u16 *value)
|
||||
void debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent,
|
||||
u16 *value)
|
||||
{
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x16,
|
||||
debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x16,
|
||||
&fops_x16_ro, &fops_x16_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_x16);
|
||||
|
@ -696,10 +669,10 @@ EXPORT_SYMBOL_GPL(debugfs_create_x16);
|
|||
* @value: a pointer to the variable that the file should read to and write
|
||||
* from.
|
||||
*/
|
||||
struct dentry *debugfs_create_x32(const char *name, umode_t mode,
|
||||
struct dentry *parent, u32 *value)
|
||||
void debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent,
|
||||
u32 *value)
|
||||
{
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x32,
|
||||
debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x32,
|
||||
&fops_x32_ro, &fops_x32_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_x32);
|
||||
|
@ -714,10 +687,10 @@ EXPORT_SYMBOL_GPL(debugfs_create_x32);
|
|||
* @value: a pointer to the variable that the file should read to and write
|
||||
* from.
|
||||
*/
|
||||
struct dentry *debugfs_create_x64(const char *name, umode_t mode,
|
||||
struct dentry *parent, u64 *value)
|
||||
void debugfs_create_x64(const char *name, umode_t mode, struct dentry *parent,
|
||||
u64 *value)
|
||||
{
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x64,
|
||||
debugfs_create_mode_unsafe(name, mode, parent, value, &fops_x64,
|
||||
&fops_x64_ro, &fops_x64_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_x64);
|
||||
|
@ -748,12 +721,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_size_t_wo, NULL, debugfs_size_t_set, "%llu\n");
|
|||
* @value: a pointer to the variable that the file should read to and write
|
||||
* from.
|
||||
*/
|
||||
struct dentry *debugfs_create_size_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, size_t *value)
|
||||
void debugfs_create_size_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, size_t *value)
|
||||
{
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value,
|
||||
&fops_size_t, &fops_size_t_ro,
|
||||
&fops_size_t_wo);
|
||||
debugfs_create_mode_unsafe(name, mode, parent, value, &fops_size_t,
|
||||
&fops_size_t_ro, &fops_size_t_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_size_t);
|
||||
|
||||
|
@ -785,12 +757,11 @@ DEFINE_DEBUGFS_ATTRIBUTE(fops_atomic_t_wo, NULL, debugfs_atomic_t_set,
|
|||
* @value: a pointer to the variable that the file should read to and write
|
||||
* from.
|
||||
*/
|
||||
struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, atomic_t *value)
|
||||
void debugfs_create_atomic_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, atomic_t *value)
|
||||
{
|
||||
return debugfs_create_mode_unsafe(name, mode, parent, value,
|
||||
&fops_atomic_t, &fops_atomic_t_ro,
|
||||
&fops_atomic_t_wo);
|
||||
debugfs_create_mode_unsafe(name, mode, parent, value, &fops_atomic_t,
|
||||
&fops_atomic_t_ro, &fops_atomic_t_wo);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(debugfs_create_atomic_t);
|
||||
|
||||
|
|
|
@ -97,28 +97,28 @@ ssize_t debugfs_attr_write(struct file *file, const char __user *buf,
|
|||
struct dentry *debugfs_rename(struct dentry *old_dir, struct dentry *old_dentry,
|
||||
struct dentry *new_dir, const char *new_name);
|
||||
|
||||
struct dentry *debugfs_create_u8(const char *name, umode_t mode,
|
||||
struct dentry *parent, u8 *value);
|
||||
struct dentry *debugfs_create_u16(const char *name, umode_t mode,
|
||||
struct dentry *parent, u16 *value);
|
||||
void debugfs_create_u8(const char *name, umode_t mode, struct dentry *parent,
|
||||
u8 *value);
|
||||
void debugfs_create_u16(const char *name, umode_t mode, struct dentry *parent,
|
||||
u16 *value);
|
||||
struct dentry *debugfs_create_u32(const char *name, umode_t mode,
|
||||
struct dentry *parent, u32 *value);
|
||||
struct dentry *debugfs_create_u64(const char *name, umode_t mode,
|
||||
struct dentry *parent, u64 *value);
|
||||
void debugfs_create_u64(const char *name, umode_t mode, struct dentry *parent,
|
||||
u64 *value);
|
||||
struct dentry *debugfs_create_ulong(const char *name, umode_t mode,
|
||||
struct dentry *parent, unsigned long *value);
|
||||
struct dentry *debugfs_create_x8(const char *name, umode_t mode,
|
||||
struct dentry *parent, u8 *value);
|
||||
struct dentry *debugfs_create_x16(const char *name, umode_t mode,
|
||||
struct dentry *parent, u16 *value);
|
||||
struct dentry *debugfs_create_x32(const char *name, umode_t mode,
|
||||
struct dentry *parent, u32 *value);
|
||||
struct dentry *debugfs_create_x64(const char *name, umode_t mode,
|
||||
struct dentry *parent, u64 *value);
|
||||
struct dentry *debugfs_create_size_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, size_t *value);
|
||||
struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, atomic_t *value);
|
||||
void debugfs_create_x8(const char *name, umode_t mode, struct dentry *parent,
|
||||
u8 *value);
|
||||
void debugfs_create_x16(const char *name, umode_t mode, struct dentry *parent,
|
||||
u16 *value);
|
||||
void debugfs_create_x32(const char *name, umode_t mode, struct dentry *parent,
|
||||
u32 *value);
|
||||
void debugfs_create_x64(const char *name, umode_t mode, struct dentry *parent,
|
||||
u64 *value);
|
||||
void debugfs_create_size_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, size_t *value);
|
||||
void debugfs_create_atomic_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, atomic_t *value);
|
||||
struct dentry *debugfs_create_bool(const char *name, umode_t mode,
|
||||
struct dentry *parent, bool *value);
|
||||
|
||||
|
@ -244,19 +244,11 @@ static inline struct dentry *debugfs_rename(struct dentry *old_dir, struct dentr
|
|||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline struct dentry *debugfs_create_u8(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
u8 *value)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
static inline void debugfs_create_u8(const char *name, umode_t mode,
|
||||
struct dentry *parent, u8 *value) { }
|
||||
|
||||
static inline struct dentry *debugfs_create_u16(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
u16 *value)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
static inline void debugfs_create_u16(const char *name, umode_t mode,
|
||||
struct dentry *parent, u16 *value) { }
|
||||
|
||||
static inline struct dentry *debugfs_create_u32(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
|
@ -265,12 +257,8 @@ static inline struct dentry *debugfs_create_u32(const char *name, umode_t mode,
|
|||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline struct dentry *debugfs_create_u64(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
u64 *value)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
static inline void debugfs_create_u64(const char *name, umode_t mode,
|
||||
struct dentry *parent, u64 *value) { }
|
||||
|
||||
static inline struct dentry *debugfs_create_ulong(const char *name,
|
||||
umode_t mode,
|
||||
|
@ -280,46 +268,26 @@ static inline struct dentry *debugfs_create_ulong(const char *name,
|
|||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
static inline struct dentry *debugfs_create_x8(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
u8 *value)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
static inline void debugfs_create_x8(const char *name, umode_t mode,
|
||||
struct dentry *parent, u8 *value) { }
|
||||
|
||||
static inline struct dentry *debugfs_create_x16(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
u16 *value)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
static inline void debugfs_create_x16(const char *name, umode_t mode,
|
||||
struct dentry *parent, u16 *value) { }
|
||||
|
||||
static inline struct dentry *debugfs_create_x32(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
u32 *value)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
static inline void debugfs_create_x32(const char *name, umode_t mode,
|
||||
struct dentry *parent, u32 *value) { }
|
||||
|
||||
static inline struct dentry *debugfs_create_x64(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
u64 *value)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
static inline void debugfs_create_x64(const char *name, umode_t mode,
|
||||
struct dentry *parent, u64 *value) { }
|
||||
|
||||
static inline struct dentry *debugfs_create_size_t(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
size_t *value)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
static inline void debugfs_create_size_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, size_t *value)
|
||||
{ }
|
||||
|
||||
static inline struct dentry *debugfs_create_atomic_t(const char *name, umode_t mode,
|
||||
struct dentry *parent, atomic_t *value)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
static inline void debugfs_create_atomic_t(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
atomic_t *value)
|
||||
{ }
|
||||
|
||||
static inline struct dentry *debugfs_create_bool(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
|
@ -383,4 +351,25 @@ static inline ssize_t debugfs_write_file_bool(struct file *file,
|
|||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* debugfs_create_xul - create a debugfs file that is used to read and write an
|
||||
* unsigned long value, formatted in hexadecimal
|
||||
* @name: a pointer to a string containing the name of the file to create.
|
||||
* @mode: the permission that the file should have
|
||||
* @parent: a pointer to the parent dentry for this file. This should be a
|
||||
* directory dentry if set. If this parameter is %NULL, then the
|
||||
* file will be created in the root of the debugfs filesystem.
|
||||
* @value: a pointer to the variable that the file should read to and write
|
||||
* from.
|
||||
*/
|
||||
static inline void debugfs_create_xul(const char *name, umode_t mode,
|
||||
struct dentry *parent,
|
||||
unsigned long *value)
|
||||
{
|
||||
if (sizeof(*value) == sizeof(u32))
|
||||
debugfs_create_x32(name, mode, parent, (u32 *)value);
|
||||
else
|
||||
debugfs_create_x64(name, mode, parent, (u64 *)value);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -80,6 +80,13 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *);
|
|||
* that generate uevents to add the environment variables.
|
||||
* @probe: Called when a new device or driver add to this bus, and callback
|
||||
* the specific driver's probe to initial the matched device.
|
||||
* @sync_state: Called to sync device state to software state after all the
|
||||
* state tracking consumers linked to this device (present at
|
||||
* the time of late_initcall) have successfully bound to a
|
||||
* driver. If the device has no consumers, this function will
|
||||
* be called at late_initcall_sync level. If the device has
|
||||
* consumers that are never bound to a driver, this function
|
||||
* will never get called until they do.
|
||||
* @remove: Called when a device removed from this bus.
|
||||
* @shutdown: Called at shut-down time to quiesce the device.
|
||||
*
|
||||
|
@ -123,6 +130,7 @@ struct bus_type {
|
|||
int (*match)(struct device *dev, struct device_driver *drv);
|
||||
int (*uevent)(struct device *dev, struct kobj_uevent_env *env);
|
||||
int (*probe)(struct device *dev);
|
||||
void (*sync_state)(struct device *dev);
|
||||
int (*remove)(struct device *dev);
|
||||
void (*shutdown)(struct device *dev);
|
||||
|
||||
|
@ -340,6 +348,13 @@ enum probe_type {
|
|||
* @probe: Called to query the existence of a specific device,
|
||||
* whether this driver can work with it, and bind the driver
|
||||
* to a specific device.
|
||||
* @sync_state: Called to sync device state to software state after all the
|
||||
* state tracking consumers linked to this device (present at
|
||||
* the time of late_initcall) have successfully bound to a
|
||||
* driver. If the device has no consumers, this function will
|
||||
* be called at late_initcall_sync level. If the device has
|
||||
* consumers that are never bound to a driver, this function
|
||||
* will never get called until they do.
|
||||
* @remove: Called when the device is removed from the system to
|
||||
* unbind a device from this driver.
|
||||
* @shutdown: Called at shut-down time to quiesce the device.
|
||||
|
@ -379,6 +394,7 @@ struct device_driver {
|
|||
const struct acpi_device_id *acpi_match_table;
|
||||
|
||||
int (*probe) (struct device *dev);
|
||||
void (*sync_state)(struct device *dev);
|
||||
int (*remove) (struct device *dev);
|
||||
void (*shutdown) (struct device *dev);
|
||||
int (*suspend) (struct device *dev, pm_message_t state);
|
||||
|
@ -946,6 +962,8 @@ extern void devm_free_pages(struct device *dev, unsigned long addr);
|
|||
|
||||
void __iomem *devm_ioremap_resource(struct device *dev,
|
||||
const struct resource *res);
|
||||
void __iomem *devm_ioremap_resource_wc(struct device *dev,
|
||||
const struct resource *res);
|
||||
|
||||
void __iomem *devm_of_iomap(struct device *dev,
|
||||
struct device_node *node, int index,
|
||||
|
@ -1080,6 +1098,7 @@ enum device_link_state {
|
|||
* AUTOREMOVE_SUPPLIER: Remove the link automatically on supplier driver unbind.
|
||||
* AUTOPROBE_CONSUMER: Probe consumer driver automatically after supplier binds.
|
||||
* MANAGED: The core tracks presence of supplier/consumer drivers (internal).
|
||||
* SYNC_STATE_ONLY: Link only affects sync_state() behavior.
|
||||
*/
|
||||
#define DL_FLAG_STATELESS BIT(0)
|
||||
#define DL_FLAG_AUTOREMOVE_CONSUMER BIT(1)
|
||||
|
@ -1088,6 +1107,7 @@ enum device_link_state {
|
|||
#define DL_FLAG_AUTOREMOVE_SUPPLIER BIT(4)
|
||||
#define DL_FLAG_AUTOPROBE_CONSUMER BIT(5)
|
||||
#define DL_FLAG_MANAGED BIT(6)
|
||||
#define DL_FLAG_SYNC_STATE_ONLY BIT(7)
|
||||
|
||||
/**
|
||||
* struct device_link - Device link representation.
|
||||
|
@ -1135,11 +1155,18 @@ enum dl_dev_state {
|
|||
* struct dev_links_info - Device data related to device links.
|
||||
* @suppliers: List of links to supplier devices.
|
||||
* @consumers: List of links to consumer devices.
|
||||
* @needs_suppliers: Hook to global list of devices waiting for suppliers.
|
||||
* @defer_sync: Hook to global list of devices that have deferred sync_state.
|
||||
* @need_for_probe: If needs_suppliers is on a list, this indicates if the
|
||||
* suppliers are needed for probe or not.
|
||||
* @status: Driver status information.
|
||||
*/
|
||||
struct dev_links_info {
|
||||
struct list_head suppliers;
|
||||
struct list_head consumers;
|
||||
struct list_head needs_suppliers;
|
||||
struct list_head defer_sync;
|
||||
bool need_for_probe;
|
||||
enum dl_dev_state status;
|
||||
};
|
||||
|
||||
|
@ -1215,6 +1242,9 @@ struct dev_links_info {
|
|||
* @offline: Set after successful invocation of bus type's .offline().
|
||||
* @of_node_reused: Set if the device-tree node is shared with an ancestor
|
||||
* device.
|
||||
* @state_synced: The hardware state of this device has been synced to match
|
||||
* the software state of this device by calling the driver/bus
|
||||
* sync_state() callback.
|
||||
* @dma_coherent: this particular device is dma coherent, even if the
|
||||
* architecture supports non-coherent devices.
|
||||
*
|
||||
|
@ -1311,6 +1341,7 @@ struct device {
|
|||
bool offline_disabled:1;
|
||||
bool offline:1;
|
||||
bool of_node_reused:1;
|
||||
bool state_synced:1;
|
||||
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \
|
||||
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \
|
||||
defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL)
|
||||
|
@ -1653,6 +1684,8 @@ struct device_link *device_link_add(struct device *consumer,
|
|||
struct device *supplier, u32 flags);
|
||||
void device_link_del(struct device_link *link);
|
||||
void device_link_remove(void *consumer, struct device *supplier);
|
||||
void device_links_supplier_sync_state_pause(void);
|
||||
void device_links_supplier_sync_state_resume(void);
|
||||
|
||||
#ifndef dev_fmt
|
||||
#define dev_fmt(fmt) fmt
|
||||
|
|
|
@ -17,6 +17,7 @@ struct device;
|
|||
struct fwnode_handle {
|
||||
struct fwnode_handle *secondary;
|
||||
const struct fwnode_operations *ops;
|
||||
struct device *dev;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -65,6 +66,43 @@ struct fwnode_reference_args {
|
|||
* endpoint node.
|
||||
* @graph_get_port_parent: Return the parent node of a port node.
|
||||
* @graph_parse_endpoint: Parse endpoint for port and endpoint id.
|
||||
* @add_links: Called after the device corresponding to the fwnode is added
|
||||
* using device_add(). The function is expected to create device
|
||||
* links to all the suppliers of the device that are available at
|
||||
* the time this function is called. The function must NOT stop
|
||||
* at the first failed device link if other unlinked supplier
|
||||
* devices are present in the system. This is necessary for the
|
||||
* driver/bus sync_state() callbacks to work correctly.
|
||||
*
|
||||
* For example, say Device-C depends on suppliers Device-S1 and
|
||||
* Device-S2 and the dependency is listed in that order in the
|
||||
* firmware. Say, S1 gets populated from the firmware after
|
||||
* late_initcall_sync(). Say S2 is populated and probed way
|
||||
* before that in device_initcall(). When C is populated, if this
|
||||
* add_links() function doesn't continue past a "failed linking to
|
||||
* S1" and continue linking C to S2, then S2 will get a
|
||||
* sync_state() callback before C is probed. This is because from
|
||||
* the perspective of S2, C was never a consumer when its
|
||||
* sync_state() evaluation is done. To avoid this, the add_links()
|
||||
* function has to go through all available suppliers of the
|
||||
* device (that corresponds to this fwnode) and link to them
|
||||
* before returning.
|
||||
*
|
||||
* If some suppliers are not yet available (indicated by an error
|
||||
* return value), this function will be called again when other
|
||||
* devices are added to allow creating device links to any newly
|
||||
* available suppliers.
|
||||
*
|
||||
* Return 0 if device links have been successfully created to all
|
||||
* the suppliers this device needs to create device links to or if
|
||||
* the supplier information is not known.
|
||||
*
|
||||
* Return -ENODEV if and only if the suppliers needed for probing
|
||||
* the device are not yet available to create device links to.
|
||||
*
|
||||
* Return -EAGAIN if there are suppliers that need to be linked to
|
||||
* that are not yet available but none of those suppliers are
|
||||
* necessary for probing this device.
|
||||
*/
|
||||
struct fwnode_operations {
|
||||
struct fwnode_handle *(*get)(struct fwnode_handle *fwnode);
|
||||
|
@ -102,6 +140,8 @@ struct fwnode_operations {
|
|||
(*graph_get_port_parent)(struct fwnode_handle *fwnode);
|
||||
int (*graph_parse_endpoint)(const struct fwnode_handle *fwnode,
|
||||
struct fwnode_endpoint *endpoint);
|
||||
int (*add_links)(const struct fwnode_handle *fwnode,
|
||||
struct device *dev);
|
||||
};
|
||||
|
||||
#define fwnode_has_op(fwnode, op) \
|
||||
|
@ -123,5 +163,6 @@ struct fwnode_operations {
|
|||
if (fwnode_has_op(fwnode, op)) \
|
||||
(fwnode)->ops->op(fwnode, ## __VA_ARGS__); \
|
||||
} while (false)
|
||||
#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev)
|
||||
|
||||
#endif
|
||||
|
|
|
@ -57,6 +57,12 @@ platform_find_device_by_driver(struct device *start,
|
|||
extern void __iomem *
|
||||
devm_platform_ioremap_resource(struct platform_device *pdev,
|
||||
unsigned int index);
|
||||
extern void __iomem *
|
||||
devm_platform_ioremap_resource_wc(struct platform_device *pdev,
|
||||
unsigned int index);
|
||||
extern void __iomem *
|
||||
devm_platform_ioremap_resource_byname(struct platform_device *pdev,
|
||||
const char *name);
|
||||
extern int platform_get_irq(struct platform_device *, unsigned int);
|
||||
extern int platform_get_irq_optional(struct platform_device *, unsigned int);
|
||||
extern int platform_irq_count(struct platform_device *);
|
||||
|
@ -294,58 +300,6 @@ void platform_unregister_drivers(struct platform_driver * const *drivers,
|
|||
#define platform_register_drivers(drivers, count) \
|
||||
__platform_register_drivers(drivers, count, THIS_MODULE)
|
||||
|
||||
/* early platform driver interface */
|
||||
struct early_platform_driver {
|
||||
const char *class_str;
|
||||
struct platform_driver *pdrv;
|
||||
struct list_head list;
|
||||
int requested_id;
|
||||
char *buffer;
|
||||
int bufsize;
|
||||
};
|
||||
|
||||
#define EARLY_PLATFORM_ID_UNSET -2
|
||||
#define EARLY_PLATFORM_ID_ERROR -3
|
||||
|
||||
extern int early_platform_driver_register(struct early_platform_driver *epdrv,
|
||||
char *buf);
|
||||
extern void early_platform_add_devices(struct platform_device **devs, int num);
|
||||
|
||||
static inline int is_early_platform_device(struct platform_device *pdev)
|
||||
{
|
||||
return !pdev->dev.driver;
|
||||
}
|
||||
|
||||
extern void early_platform_driver_register_all(char *class_str);
|
||||
extern int early_platform_driver_probe(char *class_str,
|
||||
int nr_probe, int user_only);
|
||||
extern void early_platform_cleanup(void);
|
||||
|
||||
#define early_platform_init(class_string, platdrv) \
|
||||
early_platform_init_buffer(class_string, platdrv, NULL, 0)
|
||||
|
||||
#ifndef MODULE
|
||||
#define early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \
|
||||
static __initdata struct early_platform_driver early_driver = { \
|
||||
.class_str = class_string, \
|
||||
.buffer = buf, \
|
||||
.bufsize = bufsiz, \
|
||||
.pdrv = platdrv, \
|
||||
.requested_id = EARLY_PLATFORM_ID_UNSET, \
|
||||
}; \
|
||||
static int __init early_platform_driver_setup_func(char *buffer) \
|
||||
{ \
|
||||
return early_platform_driver_register(&early_driver, buffer); \
|
||||
} \
|
||||
early_param(class_string, early_platform_driver_setup_func)
|
||||
#else /* MODULE */
|
||||
#define early_platform_init_buffer(class_string, platdrv, buf, bufsiz) \
|
||||
static inline char *early_platform_driver_setup_func(void) \
|
||||
{ \
|
||||
return bufsiz ? buf : NULL; \
|
||||
}
|
||||
#endif /* MODULE */
|
||||
|
||||
#ifdef CONFIG_SUSPEND
|
||||
extern int platform_pm_suspend(struct device *dev);
|
||||
extern int platform_pm_resume(struct device *dev);
|
||||
|
@ -380,4 +334,16 @@ extern int platform_dma_configure(struct device *dev);
|
|||
#define USE_PLATFORM_PM_SLEEP_OPS
|
||||
#endif
|
||||
|
||||
#ifndef CONFIG_SUPERH
|
||||
/*
|
||||
* REVISIT: This stub is needed for all non-SuperH users of early platform
|
||||
* drivers. It should go away once we introduce the new platform_device-based
|
||||
* early driver framework.
|
||||
*/
|
||||
static inline int is_sh_early_platform_device(struct platform_device *pdev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_SUPERH */
|
||||
|
||||
#endif /* _PLATFORM_DEVICE_H_ */
|
||||
|
|
|
@ -15,6 +15,7 @@ struct soc_device_attribute {
|
|||
const char *serial_number;
|
||||
const char *soc_id;
|
||||
const void *data;
|
||||
const struct attribute_group *custom_attr_group;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -33,6 +33,9 @@ struct gpiochip_info {
|
|||
#define GPIOLINE_FLAG_ACTIVE_LOW (1UL << 2)
|
||||
#define GPIOLINE_FLAG_OPEN_DRAIN (1UL << 3)
|
||||
#define GPIOLINE_FLAG_OPEN_SOURCE (1UL << 4)
|
||||
#define GPIOLINE_FLAG_BIAS_PULL_UP (1UL << 5)
|
||||
#define GPIOLINE_FLAG_BIAS_PULL_DOWN (1UL << 6)
|
||||
#define GPIOLINE_FLAG_BIAS_DISABLE (1UL << 7)
|
||||
|
||||
/**
|
||||
* struct gpioline_info - Information about a certain GPIO line
|
||||
|
@ -62,6 +65,9 @@ struct gpioline_info {
|
|||
#define GPIOHANDLE_REQUEST_ACTIVE_LOW (1UL << 2)
|
||||
#define GPIOHANDLE_REQUEST_OPEN_DRAIN (1UL << 3)
|
||||
#define GPIOHANDLE_REQUEST_OPEN_SOURCE (1UL << 4)
|
||||
#define GPIOHANDLE_REQUEST_BIAS_PULL_UP (1UL << 5)
|
||||
#define GPIOHANDLE_REQUEST_BIAS_PULL_DOWN (1UL << 6)
|
||||
#define GPIOHANDLE_REQUEST_BIAS_DISABLE (1UL << 7)
|
||||
|
||||
/**
|
||||
* struct gpiohandle_request - Information about a GPIO handle request
|
||||
|
@ -94,6 +100,24 @@ struct gpiohandle_request {
|
|||
int fd;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct gpiohandle_config - Configuration for a GPIO handle request
|
||||
* @flags: updated flags for the requested GPIO lines, such as
|
||||
* GPIOHANDLE_REQUEST_OUTPUT, GPIOHANDLE_REQUEST_ACTIVE_LOW etc, OR:ed
|
||||
* together
|
||||
* @default_values: if the GPIOHANDLE_REQUEST_OUTPUT is set in flags,
|
||||
* this specifies the default output value, should be 0 (low) or
|
||||
* 1 (high), anything else than 0 or 1 will be interpreted as 1 (high)
|
||||
* @padding: reserved for future use and should be zero filled
|
||||
*/
|
||||
struct gpiohandle_config {
|
||||
__u32 flags;
|
||||
__u8 default_values[GPIOHANDLES_MAX];
|
||||
__u32 padding[4]; /* padding for future use */
|
||||
};
|
||||
|
||||
#define GPIOHANDLE_SET_CONFIG_IOCTL _IOWR(0xB4, 0x0a, struct gpiohandle_config)
|
||||
|
||||
/**
|
||||
* struct gpiohandle_data - Information of values on a GPIO handle
|
||||
* @values: when getting the state of lines this contains the current
|
||||
|
|
72
lib/devres.c
72
lib/devres.c
|
@ -114,6 +114,37 @@ void devm_iounmap(struct device *dev, void __iomem *addr)
|
|||
}
|
||||
EXPORT_SYMBOL(devm_iounmap);
|
||||
|
||||
static void __iomem *
|
||||
__devm_ioremap_resource(struct device *dev, const struct resource *res,
|
||||
enum devm_ioremap_type type)
|
||||
{
|
||||
resource_size_t size;
|
||||
void __iomem *dest_ptr;
|
||||
|
||||
BUG_ON(!dev);
|
||||
|
||||
if (!res || resource_type(res) != IORESOURCE_MEM) {
|
||||
dev_err(dev, "invalid resource\n");
|
||||
return IOMEM_ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
size = resource_size(res);
|
||||
|
||||
if (!devm_request_mem_region(dev, res->start, size, dev_name(dev))) {
|
||||
dev_err(dev, "can't request region for resource %pR\n", res);
|
||||
return IOMEM_ERR_PTR(-EBUSY);
|
||||
}
|
||||
|
||||
dest_ptr = __devm_ioremap(dev, res->start, size, type);
|
||||
if (!dest_ptr) {
|
||||
dev_err(dev, "ioremap failed for resource %pR\n", res);
|
||||
devm_release_mem_region(dev, res->start, size);
|
||||
dest_ptr = IOMEM_ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
return dest_ptr;
|
||||
}
|
||||
|
||||
/**
|
||||
* devm_ioremap_resource() - check, request region, and ioremap resource
|
||||
* @dev: generic device to handle the resource for
|
||||
|
@ -134,34 +165,25 @@ EXPORT_SYMBOL(devm_iounmap);
|
|||
void __iomem *devm_ioremap_resource(struct device *dev,
|
||||
const struct resource *res)
|
||||
{
|
||||
resource_size_t size;
|
||||
void __iomem *dest_ptr;
|
||||
|
||||
BUG_ON(!dev);
|
||||
|
||||
if (!res || resource_type(res) != IORESOURCE_MEM) {
|
||||
dev_err(dev, "invalid resource\n");
|
||||
return IOMEM_ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
size = resource_size(res);
|
||||
|
||||
if (!devm_request_mem_region(dev, res->start, size, dev_name(dev))) {
|
||||
dev_err(dev, "can't request region for resource %pR\n", res);
|
||||
return IOMEM_ERR_PTR(-EBUSY);
|
||||
}
|
||||
|
||||
dest_ptr = devm_ioremap(dev, res->start, size);
|
||||
if (!dest_ptr) {
|
||||
dev_err(dev, "ioremap failed for resource %pR\n", res);
|
||||
devm_release_mem_region(dev, res->start, size);
|
||||
dest_ptr = IOMEM_ERR_PTR(-ENOMEM);
|
||||
}
|
||||
|
||||
return dest_ptr;
|
||||
return __devm_ioremap_resource(dev, res, DEVM_IOREMAP);
|
||||
}
|
||||
EXPORT_SYMBOL(devm_ioremap_resource);
|
||||
|
||||
/**
|
||||
* devm_ioremap_resource_wc() - write-combined variant of
|
||||
* devm_ioremap_resource()
|
||||
* @dev: generic device to handle the resource for
|
||||
* @res: resource to be handled
|
||||
*
|
||||
* Returns a pointer to the remapped memory or an ERR_PTR() encoded error code
|
||||
* on failure. Usage example:
|
||||
*/
|
||||
void __iomem *devm_ioremap_resource_wc(struct device *dev,
|
||||
const struct resource *res)
|
||||
{
|
||||
return __devm_ioremap_resource(dev, res, DEVM_IOREMAP_WC);
|
||||
}
|
||||
|
||||
/*
|
||||
* devm_of_iomap - Requests a resource and maps the memory mapped IO
|
||||
* for a given device_node managed by a given device
|
||||
|
|
|
@ -928,12 +928,7 @@ STA_OPS(he_capa);
|
|||
sta->debugfs_dir, sta, &sta_ ##name## _ops);
|
||||
|
||||
#define DEBUGFS_ADD_COUNTER(name, field) \
|
||||
if (sizeof(sta->field) == sizeof(u32)) \
|
||||
debugfs_create_u32(#name, 0400, sta->debugfs_dir, \
|
||||
(u32 *) &sta->field); \
|
||||
else \
|
||||
debugfs_create_u64(#name, 0400, sta->debugfs_dir, \
|
||||
(u64 *) &sta->field);
|
||||
debugfs_create_ulong(#name, 0400, sta->debugfs_dir, &sta->field);
|
||||
|
||||
void ieee80211_sta_debugfs_add(struct sta_info *sta)
|
||||
{
|
||||
|
@ -978,14 +973,8 @@ void ieee80211_sta_debugfs_add(struct sta_info *sta)
|
|||
NL80211_EXT_FEATURE_AIRTIME_FAIRNESS))
|
||||
DEBUGFS_ADD(airtime);
|
||||
|
||||
if (sizeof(sta->driver_buffered_tids) == sizeof(u32))
|
||||
debugfs_create_x32("driver_buffered_tids", 0400,
|
||||
sta->debugfs_dir,
|
||||
(u32 *)&sta->driver_buffered_tids);
|
||||
else
|
||||
debugfs_create_x64("driver_buffered_tids", 0400,
|
||||
sta->debugfs_dir,
|
||||
(u64 *)&sta->driver_buffered_tids);
|
||||
debugfs_create_xul("driver_buffered_tids", 0400, sta->debugfs_dir,
|
||||
&sta->driver_buffered_tids);
|
||||
|
||||
drv_sta_add_debugfs(local, sdata, &sta->sta, sta->debugfs_dir);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,11 @@ include ../scripts/Makefile.include
|
|||
|
||||
bindir ?= /usr/bin
|
||||
|
||||
ifeq ($(srctree),)
|
||||
# This will work when gpio is built in tools env. where srctree
|
||||
# isn't set and when invoked from selftests build, where srctree
|
||||
# is set to ".". building_out_of_srctree is undefined for in srctree
|
||||
# builds
|
||||
ifndef building_out_of_srctree
|
||||
srctree := $(patsubst %/,%,$(dir $(CURDIR)))
|
||||
srctree := $(patsubst %/,%,$(dir $(srctree)))
|
||||
endif
|
||||
|
|
Loading…
Reference in New Issue